aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshadchin <shadchin@yandex-team.com>2023-10-10 12:36:25 +0300
committershadchin <shadchin@yandex-team.com>2023-10-10 13:11:10 +0300
commita14f08a2b5d35a767003c3c71e4d6c33cff9647b (patch)
treedc6645f05a5df19ade9da6b91ba0d85070021414
parent0b9dc2e3d83dc4a141800aff0dc3a4a83e30e308 (diff)
downloadydb-a14f08a2b5d35a767003c3c71e4d6c33cff9647b.tar.gz
Split psutil on py2/py3
-rw-r--r--contrib/python/psutil/py2/.dist-info/METADATA (renamed from contrib/python/psutil/.dist-info/METADATA)0
-rw-r--r--contrib/python/psutil/py2/.dist-info/top_level.txt (renamed from contrib/python/psutil/.dist-info/top_level.txt)0
-rw-r--r--contrib/python/psutil/py2/LICENSE (renamed from contrib/python/psutil/LICENSE)0
-rw-r--r--contrib/python/psutil/py2/README.rst (renamed from contrib/python/psutil/README.rst)0
-rw-r--r--contrib/python/psutil/py2/psutil/__init__.py (renamed from contrib/python/psutil/psutil/__init__.py)0
-rw-r--r--contrib/python/psutil/py2/psutil/_common.py (renamed from contrib/python/psutil/psutil/_common.py)0
-rw-r--r--contrib/python/psutil/py2/psutil/_compat.py (renamed from contrib/python/psutil/psutil/_compat.py)0
-rw-r--r--contrib/python/psutil/py2/psutil/_pslinux.py (renamed from contrib/python/psutil/psutil/_pslinux.py)0
-rw-r--r--contrib/python/psutil/py2/psutil/_psosx.py (renamed from contrib/python/psutil/psutil/_psosx.py)0
-rw-r--r--contrib/python/psutil/py2/psutil/_psposix.py (renamed from contrib/python/psutil/psutil/_psposix.py)0
-rw-r--r--contrib/python/psutil/py2/psutil/_psutil_common.c (renamed from contrib/python/psutil/psutil/_psutil_common.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/_psutil_common.h (renamed from contrib/python/psutil/psutil/_psutil_common.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/_psutil_linux.c (renamed from contrib/python/psutil/psutil/_psutil_linux.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/_psutil_osx.c (renamed from contrib/python/psutil/psutil/_psutil_osx.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/_psutil_posix.c (renamed from contrib/python/psutil/psutil/_psutil_posix.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/_psutil_posix.h (renamed from contrib/python/psutil/psutil/_psutil_posix.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/_psutil_windows.c (renamed from contrib/python/psutil/psutil/_psutil_windows.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/_pswindows.py (renamed from contrib/python/psutil/psutil/_pswindows.py)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/aix/ifaddrs.h (renamed from contrib/python/psutil/psutil/arch/aix/ifaddrs.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/osx/process_info.c (renamed from contrib/python/psutil/psutil/arch/osx/process_info.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/osx/process_info.h (renamed from contrib/python/psutil/psutil/arch/osx/process_info.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/osx/ya.make (renamed from contrib/python/psutil/psutil/arch/osx/ya.make)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/solaris/v10/ifaddrs.h (renamed from contrib/python/psutil/psutil/arch/solaris/v10/ifaddrs.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/cpu.c (renamed from contrib/python/psutil/psutil/arch/windows/cpu.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/cpu.h (renamed from contrib/python/psutil/psutil/arch/windows/cpu.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/disk.c (renamed from contrib/python/psutil/psutil/arch/windows/disk.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/disk.h (renamed from contrib/python/psutil/psutil/arch/windows/disk.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/net.c (renamed from contrib/python/psutil/psutil/arch/windows/net.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/net.h (renamed from contrib/python/psutil/psutil/arch/windows/net.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/ntextapi.h (renamed from contrib/python/psutil/psutil/arch/windows/ntextapi.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/process_handles.c (renamed from contrib/python/psutil/psutil/arch/windows/process_handles.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/process_handles.h (renamed from contrib/python/psutil/psutil/arch/windows/process_handles.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/process_info.c (renamed from contrib/python/psutil/psutil/arch/windows/process_info.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/process_info.h (renamed from contrib/python/psutil/psutil/arch/windows/process_info.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/process_utils.c (renamed from contrib/python/psutil/psutil/arch/windows/process_utils.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/process_utils.h (renamed from contrib/python/psutil/psutil/arch/windows/process_utils.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/security.c (renamed from contrib/python/psutil/psutil/arch/windows/security.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/security.h (renamed from contrib/python/psutil/psutil/arch/windows/security.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/services.c (renamed from contrib/python/psutil/psutil/arch/windows/services.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/services.h (renamed from contrib/python/psutil/psutil/arch/windows/services.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/socks.c (renamed from contrib/python/psutil/psutil/arch/windows/socks.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/socks.h (renamed from contrib/python/psutil/psutil/arch/windows/socks.h)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/wmi.c (renamed from contrib/python/psutil/psutil/arch/windows/wmi.c)0
-rw-r--r--contrib/python/psutil/py2/psutil/arch/windows/wmi.h (renamed from contrib/python/psutil/psutil/arch/windows/wmi.h)0
-rw-r--r--contrib/python/psutil/py2/test/test.py (renamed from contrib/python/psutil/test/test.py)0
-rw-r--r--contrib/python/psutil/py2/test/ya.make8
-rw-r--r--contrib/python/psutil/py2/ya.make147
-rw-r--r--contrib/python/psutil/py3/.dist-info/METADATA560
-rw-r--r--contrib/python/psutil/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/psutil/py3/LICENSE29
-rw-r--r--contrib/python/psutil/py3/README.rst512
-rw-r--r--contrib/python/psutil/py3/psutil/__init__.py2407
-rw-r--r--contrib/python/psutil/py3/psutil/_common.py846
-rw-r--r--contrib/python/psutil/py3/psutil/_compat.py424
-rw-r--r--contrib/python/psutil/py3/psutil/_pslinux.py2157
-rw-r--r--contrib/python/psutil/py3/psutil/_psosx.py577
-rw-r--r--contrib/python/psutil/py3/psutil/_psposix.py223
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_common.c429
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_common.h158
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_linux.c555
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_osx.c1914
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_posix.c829
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_posix.h9
-rw-r--r--contrib/python/psutil/py3/psutil/_psutil_windows.c1852
-rw-r--r--contrib/python/psutil/py3/psutil/_pswindows.py1105
-rw-r--r--contrib/python/psutil/py3/psutil/arch/aix/ifaddrs.h34
-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/ya.make9
-rw-r--r--contrib/python/psutil/py3/psutil/arch/solaris/v10/ifaddrs.h26
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/cpu.c415
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/cpu.h14
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/disk.c388
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/disk.h12
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/net.c437
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/net.h11
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/ntextapi.h711
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/process_handles.c292
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/process_handles.h10
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/process_info.c798
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/process_info.h21
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/process_utils.c189
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/process_utils.h12
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/security.c138
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/security.h13
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/services.c479
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/services.h17
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/socks.c471
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/socks.h9
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/wmi.c115
-rw-r--r--contrib/python/psutil/py3/psutil/arch/windows/wmi.h10
-rw-r--r--contrib/python/psutil/py3/test/test.py4
-rw-r--r--contrib/python/psutil/py3/test/ya.make (renamed from contrib/python/psutil/test/ya.make)0
-rw-r--r--contrib/python/psutil/py3/ya.make147
-rw-r--r--contrib/python/psutil/ya.make147
95 files changed, 19934 insertions, 138 deletions
diff --git a/contrib/python/psutil/.dist-info/METADATA b/contrib/python/psutil/py2/.dist-info/METADATA
index e0d5c4d09b..e0d5c4d09b 100644
--- a/contrib/python/psutil/.dist-info/METADATA
+++ b/contrib/python/psutil/py2/.dist-info/METADATA
diff --git a/contrib/python/psutil/.dist-info/top_level.txt b/contrib/python/psutil/py2/.dist-info/top_level.txt
index a4d92cc08d..a4d92cc08d 100644
--- a/contrib/python/psutil/.dist-info/top_level.txt
+++ b/contrib/python/psutil/py2/.dist-info/top_level.txt
diff --git a/contrib/python/psutil/LICENSE b/contrib/python/psutil/py2/LICENSE
index 0bf4a7fc04..0bf4a7fc04 100644
--- a/contrib/python/psutil/LICENSE
+++ b/contrib/python/psutil/py2/LICENSE
diff --git a/contrib/python/psutil/README.rst b/contrib/python/psutil/py2/README.rst
index a9334da283..a9334da283 100644
--- a/contrib/python/psutil/README.rst
+++ b/contrib/python/psutil/py2/README.rst
diff --git a/contrib/python/psutil/psutil/__init__.py b/contrib/python/psutil/py2/psutil/__init__.py
index acd42ac264..acd42ac264 100644
--- a/contrib/python/psutil/psutil/__init__.py
+++ b/contrib/python/psutil/py2/psutil/__init__.py
diff --git a/contrib/python/psutil/psutil/_common.py b/contrib/python/psutil/py2/psutil/_common.py
index 771461d692..771461d692 100644
--- a/contrib/python/psutil/psutil/_common.py
+++ b/contrib/python/psutil/py2/psutil/_common.py
diff --git a/contrib/python/psutil/psutil/_compat.py b/contrib/python/psutil/py2/psutil/_compat.py
index 17f38485e9..17f38485e9 100644
--- a/contrib/python/psutil/psutil/_compat.py
+++ b/contrib/python/psutil/py2/psutil/_compat.py
diff --git a/contrib/python/psutil/psutil/_pslinux.py b/contrib/python/psutil/py2/psutil/_pslinux.py
index c1dc52dd5f..c1dc52dd5f 100644
--- a/contrib/python/psutil/psutil/_pslinux.py
+++ b/contrib/python/psutil/py2/psutil/_pslinux.py
diff --git a/contrib/python/psutil/psutil/_psosx.py b/contrib/python/psutil/py2/psutil/_psosx.py
index c7770d6591..c7770d6591 100644
--- a/contrib/python/psutil/psutil/_psosx.py
+++ b/contrib/python/psutil/py2/psutil/_psosx.py
diff --git a/contrib/python/psutil/psutil/_psposix.py b/contrib/python/psutil/py2/psutil/_psposix.py
index 706dab9ae8..706dab9ae8 100644
--- a/contrib/python/psutil/psutil/_psposix.py
+++ b/contrib/python/psutil/py2/psutil/_psposix.py
diff --git a/contrib/python/psutil/psutil/_psutil_common.c b/contrib/python/psutil/py2/psutil/_psutil_common.c
index f371167f3e..f371167f3e 100644
--- a/contrib/python/psutil/psutil/_psutil_common.c
+++ b/contrib/python/psutil/py2/psutil/_psutil_common.c
diff --git a/contrib/python/psutil/psutil/_psutil_common.h b/contrib/python/psutil/py2/psutil/_psutil_common.h
index cb0b399d8e..cb0b399d8e 100644
--- a/contrib/python/psutil/psutil/_psutil_common.h
+++ b/contrib/python/psutil/py2/psutil/_psutil_common.h
diff --git a/contrib/python/psutil/psutil/_psutil_linux.c b/contrib/python/psutil/py2/psutil/_psutil_linux.c
index 5836cd6b78..5836cd6b78 100644
--- a/contrib/python/psutil/psutil/_psutil_linux.c
+++ b/contrib/python/psutil/py2/psutil/_psutil_linux.c
diff --git a/contrib/python/psutil/psutil/_psutil_osx.c b/contrib/python/psutil/py2/psutil/_psutil_osx.c
index 13d0bb6856..13d0bb6856 100644
--- a/contrib/python/psutil/psutil/_psutil_osx.c
+++ b/contrib/python/psutil/py2/psutil/_psutil_osx.c
diff --git a/contrib/python/psutil/psutil/_psutil_posix.c b/contrib/python/psutil/py2/psutil/_psutil_posix.c
index 305cec76d1..305cec76d1 100644
--- a/contrib/python/psutil/psutil/_psutil_posix.c
+++ b/contrib/python/psutil/py2/psutil/_psutil_posix.c
diff --git a/contrib/python/psutil/psutil/_psutil_posix.h b/contrib/python/psutil/py2/psutil/_psutil_posix.h
index 5a37e48b15..5a37e48b15 100644
--- a/contrib/python/psutil/psutil/_psutil_posix.h
+++ b/contrib/python/psutil/py2/psutil/_psutil_posix.h
diff --git a/contrib/python/psutil/psutil/_psutil_windows.c b/contrib/python/psutil/py2/psutil/_psutil_windows.c
index a2154e923e..a2154e923e 100644
--- a/contrib/python/psutil/psutil/_psutil_windows.c
+++ b/contrib/python/psutil/py2/psutil/_psutil_windows.c
diff --git a/contrib/python/psutil/psutil/_pswindows.py b/contrib/python/psutil/py2/psutil/_pswindows.py
index 98baef5955..98baef5955 100644
--- a/contrib/python/psutil/psutil/_pswindows.py
+++ b/contrib/python/psutil/py2/psutil/_pswindows.py
diff --git a/contrib/python/psutil/psutil/arch/aix/ifaddrs.h b/contrib/python/psutil/py2/psutil/arch/aix/ifaddrs.h
index e15802bf7b..e15802bf7b 100644
--- a/contrib/python/psutil/psutil/arch/aix/ifaddrs.h
+++ b/contrib/python/psutil/py2/psutil/arch/aix/ifaddrs.h
diff --git a/contrib/python/psutil/psutil/arch/osx/process_info.c b/contrib/python/psutil/py2/psutil/arch/osx/process_info.c
index fb9f24ffac..fb9f24ffac 100644
--- a/contrib/python/psutil/psutil/arch/osx/process_info.c
+++ b/contrib/python/psutil/py2/psutil/arch/osx/process_info.c
diff --git a/contrib/python/psutil/psutil/arch/osx/process_info.h b/contrib/python/psutil/py2/psutil/arch/osx/process_info.h
index ffa6230f7c..ffa6230f7c 100644
--- a/contrib/python/psutil/psutil/arch/osx/process_info.h
+++ b/contrib/python/psutil/py2/psutil/arch/osx/process_info.h
diff --git a/contrib/python/psutil/psutil/arch/osx/ya.make b/contrib/python/psutil/py2/psutil/arch/osx/ya.make
index 613c49f924..613c49f924 100644
--- a/contrib/python/psutil/psutil/arch/osx/ya.make
+++ b/contrib/python/psutil/py2/psutil/arch/osx/ya.make
diff --git a/contrib/python/psutil/psutil/arch/solaris/v10/ifaddrs.h b/contrib/python/psutil/py2/psutil/arch/solaris/v10/ifaddrs.h
index 0953a9b99a..0953a9b99a 100644
--- a/contrib/python/psutil/psutil/arch/solaris/v10/ifaddrs.h
+++ b/contrib/python/psutil/py2/psutil/arch/solaris/v10/ifaddrs.h
diff --git a/contrib/python/psutil/psutil/arch/windows/cpu.c b/contrib/python/psutil/py2/psutil/arch/windows/cpu.c
index 18f32e5983..18f32e5983 100644
--- a/contrib/python/psutil/psutil/arch/windows/cpu.c
+++ b/contrib/python/psutil/py2/psutil/arch/windows/cpu.c
diff --git a/contrib/python/psutil/psutil/arch/windows/cpu.h b/contrib/python/psutil/py2/psutil/arch/windows/cpu.h
index d88c221210..d88c221210 100644
--- a/contrib/python/psutil/psutil/arch/windows/cpu.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/cpu.h
diff --git a/contrib/python/psutil/psutil/arch/windows/disk.c b/contrib/python/psutil/py2/psutil/arch/windows/disk.c
index 2288f22c56..2288f22c56 100644
--- a/contrib/python/psutil/psutil/arch/windows/disk.c
+++ b/contrib/python/psutil/py2/psutil/arch/windows/disk.c
diff --git a/contrib/python/psutil/psutil/arch/windows/disk.h b/contrib/python/psutil/py2/psutil/arch/windows/disk.h
index 298fb6ba0e..298fb6ba0e 100644
--- a/contrib/python/psutil/psutil/arch/windows/disk.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/disk.h
diff --git a/contrib/python/psutil/psutil/arch/windows/net.c b/contrib/python/psutil/py2/psutil/arch/windows/net.c
index 8d8f7d1c0a..8d8f7d1c0a 100644
--- a/contrib/python/psutil/psutil/arch/windows/net.c
+++ b/contrib/python/psutil/py2/psutil/arch/windows/net.c
diff --git a/contrib/python/psutil/psutil/arch/windows/net.h b/contrib/python/psutil/py2/psutil/arch/windows/net.h
index 7a6158d13b..7a6158d13b 100644
--- a/contrib/python/psutil/psutil/arch/windows/net.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/net.h
diff --git a/contrib/python/psutil/psutil/arch/windows/ntextapi.h b/contrib/python/psutil/py2/psutil/arch/windows/ntextapi.h
index a3cc932470..a3cc932470 100644
--- a/contrib/python/psutil/psutil/arch/windows/ntextapi.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/ntextapi.h
diff --git a/contrib/python/psutil/psutil/arch/windows/process_handles.c b/contrib/python/psutil/py2/psutil/arch/windows/process_handles.c
index 72e3f4d41e..72e3f4d41e 100644
--- a/contrib/python/psutil/psutil/arch/windows/process_handles.c
+++ b/contrib/python/psutil/py2/psutil/arch/windows/process_handles.c
diff --git a/contrib/python/psutil/psutil/arch/windows/process_handles.h b/contrib/python/psutil/py2/psutil/arch/windows/process_handles.h
index d1be3152d5..d1be3152d5 100644
--- a/contrib/python/psutil/psutil/arch/windows/process_handles.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/process_handles.h
diff --git a/contrib/python/psutil/psutil/arch/windows/process_info.c b/contrib/python/psutil/py2/psutil/arch/windows/process_info.c
index d44c4eb75e..d44c4eb75e 100644
--- a/contrib/python/psutil/psutil/arch/windows/process_info.c
+++ b/contrib/python/psutil/py2/psutil/arch/windows/process_info.c
diff --git a/contrib/python/psutil/psutil/arch/windows/process_info.h b/contrib/python/psutil/py2/psutil/arch/windows/process_info.h
index 5e89ddebdf..5e89ddebdf 100644
--- a/contrib/python/psutil/psutil/arch/windows/process_info.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/process_info.h
diff --git a/contrib/python/psutil/psutil/arch/windows/process_utils.c b/contrib/python/psutil/py2/psutil/arch/windows/process_utils.c
index acbda301a4..acbda301a4 100644
--- a/contrib/python/psutil/psutil/arch/windows/process_utils.c
+++ b/contrib/python/psutil/py2/psutil/arch/windows/process_utils.c
diff --git a/contrib/python/psutil/psutil/arch/windows/process_utils.h b/contrib/python/psutil/py2/psutil/arch/windows/process_utils.h
index dca7c991a5..dca7c991a5 100644
--- a/contrib/python/psutil/psutil/arch/windows/process_utils.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/process_utils.h
diff --git a/contrib/python/psutil/psutil/arch/windows/security.c b/contrib/python/psutil/py2/psutil/arch/windows/security.c
index 7e400a2541..7e400a2541 100644
--- a/contrib/python/psutil/psutil/arch/windows/security.c
+++ b/contrib/python/psutil/py2/psutil/arch/windows/security.c
diff --git a/contrib/python/psutil/psutil/arch/windows/security.h b/contrib/python/psutil/py2/psutil/arch/windows/security.h
index 8d4ddb00d4..8d4ddb00d4 100644
--- a/contrib/python/psutil/psutil/arch/windows/security.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/security.h
diff --git a/contrib/python/psutil/psutil/arch/windows/services.c b/contrib/python/psutil/py2/psutil/arch/windows/services.c
index a91cb8f797..a91cb8f797 100644
--- a/contrib/python/psutil/psutil/arch/windows/services.c
+++ b/contrib/python/psutil/py2/psutil/arch/windows/services.c
diff --git a/contrib/python/psutil/psutil/arch/windows/services.h b/contrib/python/psutil/py2/psutil/arch/windows/services.h
index ebcfa5ef59..ebcfa5ef59 100644
--- a/contrib/python/psutil/psutil/arch/windows/services.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/services.h
diff --git a/contrib/python/psutil/psutil/arch/windows/socks.c b/contrib/python/psutil/py2/psutil/arch/windows/socks.c
index 5e4c2df802..5e4c2df802 100644
--- a/contrib/python/psutil/psutil/arch/windows/socks.c
+++ b/contrib/python/psutil/py2/psutil/arch/windows/socks.c
diff --git a/contrib/python/psutil/psutil/arch/windows/socks.h b/contrib/python/psutil/py2/psutil/arch/windows/socks.h
index cd9ba58dcb..cd9ba58dcb 100644
--- a/contrib/python/psutil/psutil/arch/windows/socks.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/socks.h
diff --git a/contrib/python/psutil/psutil/arch/windows/wmi.c b/contrib/python/psutil/py2/psutil/arch/windows/wmi.c
index f9a847d3bf..f9a847d3bf 100644
--- a/contrib/python/psutil/psutil/arch/windows/wmi.c
+++ b/contrib/python/psutil/py2/psutil/arch/windows/wmi.c
diff --git a/contrib/python/psutil/psutil/arch/windows/wmi.h b/contrib/python/psutil/py2/psutil/arch/windows/wmi.h
index 311242a393..311242a393 100644
--- a/contrib/python/psutil/psutil/arch/windows/wmi.h
+++ b/contrib/python/psutil/py2/psutil/arch/windows/wmi.h
diff --git a/contrib/python/psutil/test/test.py b/contrib/python/psutil/py2/test/test.py
index 4f5a0e50d8..4f5a0e50d8 100644
--- a/contrib/python/psutil/test/test.py
+++ b/contrib/python/psutil/py2/test/test.py
diff --git a/contrib/python/psutil/py2/test/ya.make b/contrib/python/psutil/py2/test/ya.make
new file mode 100644
index 0000000000..20d09c1d70
--- /dev/null
+++ b/contrib/python/psutil/py2/test/ya.make
@@ -0,0 +1,8 @@
+PY2TEST()
+PEERDIR(
+ contrib/python/psutil
+ library/python/import_test
+)
+TEST_SRCS(test.py)
+NO_LINT()
+END()
diff --git a/contrib/python/psutil/py2/ya.make b/contrib/python/psutil/py2/ya.make
new file mode 100644
index 0000000000..ddc88f1692
--- /dev/null
+++ b/contrib/python/psutil/py2/ya.make
@@ -0,0 +1,147 @@
+PY2_LIBRARY()
+
+LICENSE(BSD-3-Clause)
+
+VERSION(5.8.0)
+
+NO_UTIL()
+
+SRCDIR(contrib/python/psutil/py2/psutil)
+
+NO_COMPILER_WARNINGS()
+
+CFLAGS(
+ -DPSUTIL_VERSION=580
+)
+
+IF (OS_LINUX OR OS_DARWIN)
+ CFLAGS(
+ -DPSUTIL_POSIX=1
+ )
+ SRCS(
+ _psutil_common.c
+ _psutil_posix.c
+ )
+ PY_REGISTER(psutil._psutil_posix)
+ENDIF ()
+
+IF (OS_LINUX)
+ CFLAGS(
+ -DPSUTIL_LINUX=1
+ )
+
+ SRCS(
+ _psutil_linux.c
+ )
+ PY_REGISTER(psutil._psutil_linux)
+ENDIF ()
+
+IF (OS_DARWIN)
+ CFLAGS(
+ -DPSUTIL_OSX=1
+ )
+
+ EXTRALIBS("-framework CoreFoundation -framework IOKit")
+
+ PEERDIR(
+ contrib/python/psutil/py2/psutil/arch/osx
+ )
+
+ SRCS(
+ _psutil_osx.c
+ )
+
+ PY_REGISTER(psutil._psutil_osx)
+ENDIF ()
+
+IF (OS_WINDOWS)
+ CFLAGS(
+ -DPSUTIL_WINDOWS=1
+ -DPSUTIL_SIZEOF_PID_T=4
+ )
+
+ LDFLAGS(
+ Shell32.lib
+ PowrProf.lib
+ Wtsapi32.lib
+ Pdh.lib
+ )
+
+ SRCS(
+ _psutil_common.c
+ _psutil_windows.c
+ arch/windows/cpu.c
+ arch/windows/disk.c
+ arch/windows/net.c
+ arch/windows/process_handles.c
+ arch/windows/process_info.c
+ arch/windows/process_utils.c
+ arch/windows/security.c
+ arch/windows/services.c
+ arch/windows/socks.c
+ arch/windows/wmi.c
+ )
+
+ PY_REGISTER(psutil._psutil_windows)
+ENDIF ()
+
+NO_CHECK_IMPORTS(
+ psutil._psbsd
+ psutil._psosx
+ psutil._pssunos
+ psutil._psutil_bsd
+ psutil._psutil_common
+ psutil._psutil_osx
+ psutil._psutil_sunos
+ psutil._psutil_windows
+ psutil._pswindows
+)
+
+PY_SRCS(
+ TOP_LEVEL
+ psutil/__init__.py
+ psutil/_common.py
+ psutil/_compat.py
+)
+
+IF (OS_LINUX OR OS_DARWIN)
+ PY_SRCS(
+ TOP_LEVEL
+ psutil/_psposix.py
+ )
+ENDIF ()
+
+IF (OS_LINUX)
+ PY_SRCS(
+ TOP_LEVEL
+ psutil/_pslinux.py
+ )
+ENDIF ()
+
+IF (OS_DARWIN)
+ PY_SRCS(
+ TOP_LEVEL
+ psutil/_psosx.py
+ )
+ENDIF ()
+
+IF (OS_WINDOWS)
+ PY_SRCS(
+ TOP_LEVEL
+ psutil/_pswindows.py
+ )
+ENDIF ()
+
+RESOURCE_FILES(
+ PREFIX contrib/python/psutil/py2/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+NO_LINT()
+
+END()
+
+RECURSE_FOR_TESTS(
+ test
+)
diff --git a/contrib/python/psutil/py3/.dist-info/METADATA b/contrib/python/psutil/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..e0d5c4d09b
--- /dev/null
+++ b/contrib/python/psutil/py3/.dist-info/METADATA
@@ -0,0 +1,560 @@
+Metadata-Version: 2.1
+Name: psutil
+Version: 5.8.0
+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
+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
+Classifier: Environment :: Console
+Classifier: Environment :: Win32 (MS Windows)
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Information Technology
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: Microsoft :: Windows :: Windows 10
+Classifier: Operating System :: Microsoft :: Windows :: Windows 7
+Classifier: Operating System :: Microsoft :: Windows :: Windows 8
+Classifier: Operating System :: Microsoft :: Windows :: Windows 8.1
+Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2003
+Classifier: Operating System :: Microsoft :: Windows :: Windows Server 2008
+Classifier: Operating System :: Microsoft :: Windows :: Windows Vista
+Classifier: Operating System :: Microsoft
+Classifier: Operating System :: OS Independent
+Classifier: Operating System :: POSIX :: AIX
+Classifier: Operating System :: POSIX :: BSD :: FreeBSD
+Classifier: Operating System :: POSIX :: BSD :: NetBSD
+Classifier: Operating System :: POSIX :: BSD :: OpenBSD
+Classifier: Operating System :: POSIX :: BSD
+Classifier: Operating System :: POSIX :: Linux
+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
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: System :: Benchmark
+Classifier: Topic :: System :: Hardware :: Hardware Drivers
+Classifier: Topic :: System :: Hardware
+Classifier: Topic :: System :: Monitoring
+Classifier: Topic :: System :: Networking :: Monitoring :: Hardware Watchdog
+Classifier: Topic :: System :: Networking :: Monitoring
+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.*
+Description-Content-Type: text/x-rst
+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|
+| |version| |py-versions| |packages| |license|
+| |github-actions| |appveyor| |doc| |twitter| |tidelift|
+
+.. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg
+ :target: https://pepy.tech/project/psutil
+ :alt: Downloads
+
+.. |stars| image:: https://img.shields.io/github/stars/giampaolo/psutil.svg
+ :target: https://github.com/giampaolo/psutil/stargazers
+ :alt: Github stars
+
+.. |forks| image:: https://img.shields.io/github/forks/giampaolo/psutil.svg
+ :target: https://github.com/giampaolo/psutil/network/members
+ :alt: Github forks
+
+.. |contributors| image:: https://img.shields.io/github/contributors/giampaolo/psutil.svg
+ :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| 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
+
+.. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows
+ :target: https://ci.appveyor.com/project/giampaolo/psutil
+ :alt: Windows tests (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
+ :alt: Documentation Status
+
+.. |version| image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi
+ :target: https://pypi.org/project/psutil
+ :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
+ :target: https://repology.org/metapackage/python:psutil/versions
+ :alt: Binary packages
+
+.. |license| image:: https://img.shields.io/pypi/l/psutil.svg
+ :target: https://github.com/giampaolo/psutil/blob/master/LICENSE
+ :alt: License
+
+.. |twitter| image:: https://img.shields.io/twitter/follow/grodola.svg?label=follow&style=flat&logo=twitter&logoColor=4FADFF
+ :target: https://twitter.com/grodola
+ :alt: Twitter Follow
+
+.. |tidelift| image:: https://tidelift.com/badges/github/giampaolo/psutil?style=flat
+ :target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
+ :alt: Tidelift
+
+-----
+
+Quick links
+===========
+
+- `Home page <https://github.com/giampaolo/psutil>`_
+- `Install <https://github.com/giampaolo/psutil/blob/master/INSTALL.rst>`_
+- `Documentation <http://psutil.readthedocs.io>`_
+- `Download <https://pypi.org/project/psutil/#files>`_
+- `Forum <http://groups.google.com/group/psutil/topics>`_
+- `StackOverflow <https://stackoverflow.com/questions/tagged/psutil>`_
+- `Blog <https://gmpy.dev/tags/psutil>`_
+- `What's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst>`_
+
+
+Summary
+=======
+
+psutil (process and system utilities) is a cross-platform library for
+retrieving information on **running processes** and **system utilization**
+(CPU, memory, disks, network, sensors) in Python.
+It is useful mainly for **system monitoring**, **profiling and limiting process
+resources** and **management of running processes**.
+It implements many functionalities offered by classic UNIX command line tools
+such as *ps, top, iotop, lsof, netstat, ifconfig, free* and others.
+psutil currently supports the following platforms:
+
+- **Linux**
+- **Windows**
+- **macOS**
+- **FreeBSD, OpenBSD**, **NetBSD**
+- **Sun Solaris**
+- **AIX**
+
+Supported Python versions are **2.6**, **2.7**, **3.4+** and
+`PyPy <http://pypy.org/>`__.
+
+Funding
+=======
+
+While psutil is free software and will always be, the project would benefit
+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>`__,
+`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>`__.
+
+Sponsors
+========
+
+.. image:: https://github.com/giampaolo/psutil/raw/master/docs/_static/tidelift-logo.png
+ :width: 200
+ :alt: Alternative text
+
+`Add your logo <https://github.com/sponsors/giampaolo>`__.
+
+Example usages
+==============
+
+This represents pretty much the whole psutil API.
+
+CPU
+---
+
+.. code-block:: python
+
+ >>> 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)
+ >>>
+ >>> for x in range(3):
+ ... psutil.cpu_percent(interval=1)
+ ...
+ 4.0
+ 5.9
+ 3.8
+ >>>
+ >>> for x in range(3):
+ ... psutil.cpu_percent(interval=1, percpu=True)
+ ...
+ [4.0, 6.9, 3.7, 9.2]
+ [7.0, 8.5, 2.4, 2.1]
+ [1.2, 9.0, 9.9, 7.2]
+ >>>
+ >>> for x in range(3):
+ ... psutil.cpu_times_percent(interval=1, percpu=False)
+ ...
+ scputimes(user=1.5, nice=0.0, system=0.5, idle=96.5, iowait=1.5, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
+ scputimes(user=1.0, nice=0.0, system=0.0, idle=99.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
+ scputimes(user=2.0, nice=0.0, system=0.0, idle=98.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
+ >>>
+ >>> psutil.cpu_count()
+ 4
+ >>> psutil.cpu_count(logical=False)
+ 2
+ >>>
+ >>> psutil.cpu_stats()
+ scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0)
+ >>>
+ >>> psutil.cpu_freq()
+ scpufreq(current=931.42925, min=800.0, max=3500.0)
+ >>>
+ >>> psutil.getloadavg() # also on Windows (emulated)
+ (3.14, 3.89, 4.67)
+
+Memory
+------
+
+.. code-block:: python
+
+ >>> psutil.virtual_memory()
+ svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304)
+ >>> psutil.swap_memory()
+ sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944)
+ >>>
+
+Disks
+-----
+
+.. code-block:: python
+
+ >>> 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)]
+ >>>
+ >>> psutil.disk_usage('/')
+ sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
+ >>>
+ >>> psutil.disk_io_counters(perdisk=False)
+ sdiskio(read_count=719566, write_count=1082197, read_bytes=18626220032, write_bytes=24081764352, read_time=5023392, write_time=63199568, read_merged_count=619166, write_merged_count=812396, busy_time=4523412)
+ >>>
+
+Network
+-------
+
+.. code-block:: python
+
+ >>> psutil.net_io_counters(pernic=True)
+ {'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0),
+ 'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)}
+ >>>
+ >>> psutil.net_connections(kind='tcp')
+ [sconn(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', pid=1254),
+ sconn(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', pid=2987),
+ ...]
+ >>>
+ >>> psutil.net_if_addrs()
+ {'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None),
+ snicaddr(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),
+ snicaddr(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)],
+ 'wlan0': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
+ snicaddr(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),
+ 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)}
+ >>>
+
+Sensors
+-------
+
+.. code-block:: python
+
+ >>> import psutil
+ >>> psutil.sensors_temperatures()
+ {'acpitz': [shwtemp(label='', current=47.0, high=103.0, critical=103.0)],
+ 'asus': [shwtemp(label='', current=47.0, high=None, critical=None)],
+ 'coretemp': [shwtemp(label='Physical id 0', current=52.0, high=100.0, critical=100.0),
+ shwtemp(label='Core 0', current=45.0, high=100.0, critical=100.0)]}
+ >>>
+ >>> psutil.sensors_fans()
+ {'asus': [sfan(label='cpu_fan', current=3200)]}
+ >>>
+ >>> psutil.sensors_battery()
+ sbattery(percent=93, secsleft=16628, power_plugged=False)
+ >>>
+
+Other system info
+-----------------
+
+.. code-block:: python
+
+ >>> import psutil
+ >>> psutil.users()
+ [suser(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0, pid=1352),
+ suser(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0, pid=1788)]
+ >>>
+ >>> psutil.boot_time()
+ 1365519115.0
+ >>>
+
+Process management
+------------------
+
+.. code-block:: python
+
+ >>> import psutil
+ >>> psutil.pids()
+ [1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, 268, 1215,
+ 1216, 1220, 1221, 1243, 1244, 1301, 1601, 2237, 2355, 2637, 2774, 3932,
+ 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, 4263, 4282, 4306, 4311,
+ 4312, 4313, 4314, 4337, 4339, 4357, 4358, 4363, 4383, 4395, 4408, 4433,
+ 4443, 4445, 4446, 5167, 5234, 5235, 5252, 5318, 5424, 5644, 6987, 7054,
+ 7055, 7071]
+ >>>
+ >>> p = psutil.Process(7055)
+ >>> p
+ psutil.Process(pid=7055, name='python3', status='running', started='09:04:44')
+ >>> p.name()
+ 'python'
+ >>> p.exe()
+ '/usr/bin/python'
+ >>> p.cwd()
+ '/home/giampaolo'
+ >>> p.cmdline()
+ ['/usr/bin/python', '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.status()
+ 'running'
+ >>> p.username()
+ 'giampaolo'
+ >>> p.create_time()
+ 1267551141.5019531
+ >>> p.terminal()
+ '/dev/pts/0'
+ >>>
+ >>> p.uids()
+ puids(real=1000, effective=1000, saved=1000)
+ >>> p.gids()
+ pgids(real=1000, effective=1000, saved=1000)
+ >>>
+ >>> p.cpu_times()
+ pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1, iowait=0.0)
+ >>> p.cpu_percent(interval=1.0)
+ 12.1
+ >>> p.cpu_affinity()
+ [0, 1, 2, 3]
+ >>> p.cpu_affinity([0, 1]) # set
+ >>> p.cpu_num()
+ 1
+ >>>
+ >>> p.memory_info()
+ pmem(rss=10915840, vms=67608576, shared=3313664, text=2310144, lib=0, data=7262208, dirty=0)
+ >>> p.memory_full_info() # "real" USS memory usage (Linux, macOS, Win only)
+ pfullmem(rss=10199040, vms=52133888, shared=3887104, text=2867200, lib=0, data=5967872, dirty=0, uss=6545408, pss=6872064, swap=0)
+ >>> p.memory_percent()
+ 0.7823
+ >>> p.memory_maps()
+ [pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0),
+ pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0),
+ pmmap_grouped(path='[heap]', rss=32768, size=139264, pss=32768, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=32768, referenced=32768, anonymous=32768, swap=0),
+ pmmap_grouped(path='[stack]', rss=2465792, size=2494464, pss=2465792, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=2465792, referenced=2277376, anonymous=2465792, swap=0),
+ ...]
+ >>>
+ >>> p.io_counters()
+ pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543)
+ >>>
+ >>> p.open_files()
+ [popenfile(path='/home/giampaolo/monit.py', fd=3, position=0, mode='r', flags=32768),
+ popenfile(path='/var/log/monit.log', fd=4, position=235542, mode='a', flags=33793)]
+ >>>
+ >>> p.connections(kind='tcp')
+ [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_ctx_switches()
+ pctxsw(voluntary=78, involuntary=19)
+ >>>
+ >>> p.nice()
+ 0
+ >>> p.nice(10) # set
+ >>>
+ >>> p.ionice(psutil.IOPRIO_CLASS_IDLE) # IO priority (Win and Linux only)
+ >>> p.ionice()
+ pionice(ioclass=<IOPriority.IOPRIO_CLASS_IDLE: 3>, value=0)
+ >>>
+ >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) # set resource limits (Linux only)
+ >>> p.rlimit(psutil.RLIMIT_NOFILE)
+ (5, 5)
+ >>>
+ >>> p.environ()
+ {'LC_PAPER': 'it_IT.UTF-8', 'SHELL': '/bin/bash', 'GREP_OPTIONS': '--color=auto',
+ 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg',
+ ...}
+ >>>
+ >>> p.as_dict()
+ {'status': 'running', 'num_ctx_switches': pctxsw(voluntary=63, involuntary=1), 'pid': 5457, ...}
+ >>> p.is_running()
+ True
+ >>> p.suspend()
+ >>> p.resume()
+ >>>
+ >>> p.terminate()
+ >>> p.kill()
+ >>> p.wait(timeout=3)
+ <Exitcode.EX_OK: 0>
+ >>>
+ >>> psutil.test()
+ USER PID %CPU %MEM VSZ RSS TTY START TIME COMMAND
+ root 1 0.0 0.0 24584 2240 Jun17 00:00 init
+ root 2 0.0 0.0 0 0 Jun17 00:00 kthreadd
+ ...
+ giampaolo 31475 0.0 0.0 20760 3024 /dev/pts/0 Jun19 00:00 python2.4
+ giampaolo 31721 0.0 2.2 773060 181896 00:04 10:30 chrome
+ root 31763 0.0 0.0 0 0 00:05 00:00 kworker/0:1
+ >>>
+
+Further process APIs
+--------------------
+
+.. code-block:: python
+
+ >>> import psutil
+ >>> for proc in psutil.process_iter(['pid', 'name']):
+ ... print(proc.info)
+ ...
+ {'pid': 1, 'name': 'systemd'}
+ {'pid': 2, 'name': 'kthreadd'}
+ {'pid': 3, 'name': 'ksoftirqd/0'}
+ ...
+ >>>
+ >>> psutil.pid_exists(3)
+ True
+ >>>
+ >>> def on_terminate(proc):
+ ... print("process {} terminated".format(proc))
+ ...
+ >>> # waits for multiple processes to terminate
+ >>> 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
+----------------
+
+.. code-block:: python
+
+ >>> list(psutil.win_service_iter())
+ [<WindowsService(name='AeLookupSvc', display_name='Application Experience') at 38850096>,
+ <WindowsService(name='ALG', display_name='Application Layer Gateway Service') at 38850128>,
+ <WindowsService(name='APNMCP', display_name='Ask Update Service') at 38850160>,
+ <WindowsService(name='AppIDSvc', display_name='Application Identity') at 38850192>,
+ ...]
+ >>> s = psutil.win_service_get('alg')
+ >>> s.as_dict()
+ {'binpath': 'C:\\Windows\\System32\\alg.exe',
+ 'description': 'Provides support for 3rd party protocol plug-ins for Internet Connection Sharing',
+ 'display_name': 'Application Layer Gateway Service',
+ 'name': 'alg',
+ 'pid': None,
+ 'start_type': 'manual',
+ 'status': 'stopped',
+ 'username': 'NT AUTHORITY\\LocalService'}
+
+Projects using psutil
+=====================
+
+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/Jahaja/psdash
+- https://github.com/ajenti/ajenti
+- https://github.com/home-assistant/home-assistant/
+
+Portings
+========
+
+- Go: https://github.com/shirou/gopsutil
+- 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/.dist-info/top_level.txt b/contrib/python/psutil/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..a4d92cc08d
--- /dev/null
+++ b/contrib/python/psutil/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+psutil
diff --git a/contrib/python/psutil/py3/LICENSE b/contrib/python/psutil/py3/LICENSE
new file mode 100644
index 0000000000..0bf4a7fc04
--- /dev/null
+++ b/contrib/python/psutil/py3/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola'
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the psutil authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/contrib/python/psutil/py3/README.rst b/contrib/python/psutil/py3/README.rst
new file mode 100644
index 0000000000..a9334da283
--- /dev/null
+++ b/contrib/python/psutil/py3/README.rst
@@ -0,0 +1,512 @@
+| |downloads| |stars| |forks| |contributors| |coverage| |quality|
+| |version| |py-versions| |packages| |license|
+| |github-actions| |appveyor| |doc| |twitter| |tidelift|
+
+.. |downloads| image:: https://img.shields.io/pypi/dm/psutil.svg
+ :target: https://pepy.tech/project/psutil
+ :alt: Downloads
+
+.. |stars| image:: https://img.shields.io/github/stars/giampaolo/psutil.svg
+ :target: https://github.com/giampaolo/psutil/stargazers
+ :alt: Github stars
+
+.. |forks| image:: https://img.shields.io/github/forks/giampaolo/psutil.svg
+ :target: https://github.com/giampaolo/psutil/network/members
+ :alt: Github forks
+
+.. |contributors| image:: https://img.shields.io/github/contributors/giampaolo/psutil.svg
+ :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| 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
+
+.. |appveyor| image:: https://img.shields.io/appveyor/ci/giampaolo/psutil/master.svg?maxAge=3600&label=Windows
+ :target: https://ci.appveyor.com/project/giampaolo/psutil
+ :alt: Windows tests (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
+ :alt: Documentation Status
+
+.. |version| image:: https://img.shields.io/pypi/v/psutil.svg?label=pypi
+ :target: https://pypi.org/project/psutil
+ :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
+ :target: https://repology.org/metapackage/python:psutil/versions
+ :alt: Binary packages
+
+.. |license| image:: https://img.shields.io/pypi/l/psutil.svg
+ :target: https://github.com/giampaolo/psutil/blob/master/LICENSE
+ :alt: License
+
+.. |twitter| image:: https://img.shields.io/twitter/follow/grodola.svg?label=follow&style=flat&logo=twitter&logoColor=4FADFF
+ :target: https://twitter.com/grodola
+ :alt: Twitter Follow
+
+.. |tidelift| image:: https://tidelift.com/badges/github/giampaolo/psutil?style=flat
+ :target: https://tidelift.com/subscription/pkg/pypi-psutil?utm_source=pypi-psutil&utm_medium=referral&utm_campaign=readme
+ :alt: Tidelift
+
+-----
+
+.. raw:: html
+
+ <div align="center">
+ <a href="https://github.com/giampaolo/psutil"><img src="https://github.com/giampaolo/psutil/raw/master/docs/_static/psutil-logo.png" /></a>
+ <br />
+ <br />
+ <a href="https://github.com/giampaolo/psutil"><b>Home</b></a>&nbsp;&nbsp;&nbsp;
+ <a href="https://github.com/giampaolo/psutil/blob/master/INSTALL.rst"><b>Install</b></a>&nbsp;&nbsp;&nbsp;
+ <a href="https://psutil.readthedocs.io/"><b>Documentation</b></a>&nbsp;&nbsp;&nbsp;
+ <a href="https://pypi.org/project/psutil/#files"><b>Download</b></a>&nbsp;&nbsp;&nbsp;
+ <a href="https://groups.google.com/g/psutil"><b>Forum</b></a>&nbsp;&nbsp;&nbsp;
+ <a href="https://gmpy.dev/tags/psutil"><b>Blog</b></a>&nbsp;&nbsp;&nbsp;
+ <a href="#funding"><b>Funding</b></a>&nbsp;&nbsp;&nbsp;
+ <a href="https://github.com/giampaolo/psutil/blob/master/HISTORY.rst"><b>What's new</b></a>&nbsp;&nbsp;&nbsp;
+ </div>
+
+Summary
+=======
+
+psutil (process and system utilities) is a cross-platform library for
+retrieving information on **running processes** and **system utilization**
+(CPU, memory, disks, network, sensors) in Python.
+It is useful mainly for **system monitoring**, **profiling and limiting process
+resources** and **management of running processes**.
+It implements many functionalities offered by classic UNIX command line tools
+such as *ps, top, iotop, lsof, netstat, ifconfig, free* and others.
+psutil currently supports the following platforms:
+
+- **Linux**
+- **Windows**
+- **macOS**
+- **FreeBSD, OpenBSD**, **NetBSD**
+- **Sun Solaris**
+- **AIX**
+
+Supported Python versions are **2.6**, **2.7**, **3.4+** and
+`PyPy <http://pypy.org/>`__.
+
+Funding
+=======
+
+While psutil is free software and will always be, the project would benefit
+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>`__,
+`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>`__.
+
+Sponsors
+========
+
+.. raw:: html
+
+ <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" />
+ </a>
+ </div>
+ <sup><a href="https://github.com/sponsors/giampaolo">add your logo</a></sup>
+
+Supporters
+==========
+
+None yet.
+
+.. raw:: html
+
+ <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.
+
+Example usages
+==============
+
+This represents pretty much the whole psutil API.
+
+CPU
+---
+
+.. code-block:: python
+
+ >>> 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)
+ >>>
+ >>> for x in range(3):
+ ... psutil.cpu_percent(interval=1)
+ ...
+ 4.0
+ 5.9
+ 3.8
+ >>>
+ >>> for x in range(3):
+ ... psutil.cpu_percent(interval=1, percpu=True)
+ ...
+ [4.0, 6.9, 3.7, 9.2]
+ [7.0, 8.5, 2.4, 2.1]
+ [1.2, 9.0, 9.9, 7.2]
+ >>>
+ >>> for x in range(3):
+ ... psutil.cpu_times_percent(interval=1, percpu=False)
+ ...
+ scputimes(user=1.5, nice=0.0, system=0.5, idle=96.5, iowait=1.5, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
+ scputimes(user=1.0, nice=0.0, system=0.0, idle=99.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
+ scputimes(user=2.0, nice=0.0, system=0.0, idle=98.0, iowait=0.0, irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
+ >>>
+ >>> psutil.cpu_count()
+ 4
+ >>> psutil.cpu_count(logical=False)
+ 2
+ >>>
+ >>> psutil.cpu_stats()
+ scpustats(ctx_switches=20455687, interrupts=6598984, soft_interrupts=2134212, syscalls=0)
+ >>>
+ >>> psutil.cpu_freq()
+ scpufreq(current=931.42925, min=800.0, max=3500.0)
+ >>>
+ >>> psutil.getloadavg() # also on Windows (emulated)
+ (3.14, 3.89, 4.67)
+
+Memory
+------
+
+.. code-block:: python
+
+ >>> psutil.virtual_memory()
+ svmem(total=10367352832, available=6472179712, percent=37.6, used=8186245120, free=2181107712, active=4748992512, inactive=2758115328, buffers=790724608, cached=3500347392, shared=787554304)
+ >>> psutil.swap_memory()
+ sswap(total=2097147904, used=296128512, free=1801019392, percent=14.1, sin=304193536, sout=677842944)
+ >>>
+
+Disks
+-----
+
+.. code-block:: python
+
+ >>> 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)]
+ >>>
+ >>> psutil.disk_usage('/')
+ sdiskusage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
+ >>>
+ >>> psutil.disk_io_counters(perdisk=False)
+ sdiskio(read_count=719566, write_count=1082197, read_bytes=18626220032, write_bytes=24081764352, read_time=5023392, write_time=63199568, read_merged_count=619166, write_merged_count=812396, busy_time=4523412)
+ >>>
+
+Network
+-------
+
+.. code-block:: python
+
+ >>> psutil.net_io_counters(pernic=True)
+ {'eth0': netio(bytes_sent=485291293, bytes_recv=6004858642, packets_sent=3251564, packets_recv=4787798, errin=0, errout=0, dropin=0, dropout=0),
+ 'lo': netio(bytes_sent=2838627, bytes_recv=2838627, packets_sent=30567, packets_recv=30567, errin=0, errout=0, dropin=0, dropout=0)}
+ >>>
+ >>> psutil.net_connections(kind='tcp')
+ [sconn(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', pid=1254),
+ sconn(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', pid=2987),
+ ...]
+ >>>
+ >>> psutil.net_if_addrs()
+ {'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast='127.0.0.1', ptp=None),
+ snicaddr(family=<AddressFamily.AF_INET6: 10>, address='::1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None),
+ snicaddr(family=<AddressFamily.AF_LINK: 17>, address='00:00:00:00:00:00', netmask=None, broadcast='00:00:00:00:00:00', ptp=None)],
+ 'wlan0': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='192.168.1.3', netmask='255.255.255.0', broadcast='192.168.1.255', ptp=None),
+ snicaddr(family=<AddressFamily.AF_INET6: 10>, address='fe80::c685:8ff:fe45:641%wlan0', netmask='ffff:ffff:ffff:ffff::', broadcast=None, ptp=None),
+ 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)}
+ >>>
+
+Sensors
+-------
+
+.. code-block:: python
+
+ >>> import psutil
+ >>> psutil.sensors_temperatures()
+ {'acpitz': [shwtemp(label='', current=47.0, high=103.0, critical=103.0)],
+ 'asus': [shwtemp(label='', current=47.0, high=None, critical=None)],
+ 'coretemp': [shwtemp(label='Physical id 0', current=52.0, high=100.0, critical=100.0),
+ shwtemp(label='Core 0', current=45.0, high=100.0, critical=100.0)]}
+ >>>
+ >>> psutil.sensors_fans()
+ {'asus': [sfan(label='cpu_fan', current=3200)]}
+ >>>
+ >>> psutil.sensors_battery()
+ sbattery(percent=93, secsleft=16628, power_plugged=False)
+ >>>
+
+Other system info
+-----------------
+
+.. code-block:: python
+
+ >>> import psutil
+ >>> psutil.users()
+ [suser(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0, pid=1352),
+ suser(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0, pid=1788)]
+ >>>
+ >>> psutil.boot_time()
+ 1365519115.0
+ >>>
+
+Process management
+------------------
+
+.. code-block:: python
+
+ >>> import psutil
+ >>> psutil.pids()
+ [1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, 268, 1215,
+ 1216, 1220, 1221, 1243, 1244, 1301, 1601, 2237, 2355, 2637, 2774, 3932,
+ 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, 4263, 4282, 4306, 4311,
+ 4312, 4313, 4314, 4337, 4339, 4357, 4358, 4363, 4383, 4395, 4408, 4433,
+ 4443, 4445, 4446, 5167, 5234, 5235, 5252, 5318, 5424, 5644, 6987, 7054,
+ 7055, 7071]
+ >>>
+ >>> p = psutil.Process(7055)
+ >>> p
+ psutil.Process(pid=7055, name='python3', status='running', started='09:04:44')
+ >>> p.name()
+ 'python'
+ >>> p.exe()
+ '/usr/bin/python'
+ >>> p.cwd()
+ '/home/giampaolo'
+ >>> p.cmdline()
+ ['/usr/bin/python', '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.status()
+ 'running'
+ >>> p.username()
+ 'giampaolo'
+ >>> p.create_time()
+ 1267551141.5019531
+ >>> p.terminal()
+ '/dev/pts/0'
+ >>>
+ >>> p.uids()
+ puids(real=1000, effective=1000, saved=1000)
+ >>> p.gids()
+ pgids(real=1000, effective=1000, saved=1000)
+ >>>
+ >>> p.cpu_times()
+ pcputimes(user=1.02, system=0.31, children_user=0.32, children_system=0.1, iowait=0.0)
+ >>> p.cpu_percent(interval=1.0)
+ 12.1
+ >>> p.cpu_affinity()
+ [0, 1, 2, 3]
+ >>> p.cpu_affinity([0, 1]) # set
+ >>> p.cpu_num()
+ 1
+ >>>
+ >>> p.memory_info()
+ pmem(rss=10915840, vms=67608576, shared=3313664, text=2310144, lib=0, data=7262208, dirty=0)
+ >>> p.memory_full_info() # "real" USS memory usage (Linux, macOS, Win only)
+ pfullmem(rss=10199040, vms=52133888, shared=3887104, text=2867200, lib=0, data=5967872, dirty=0, uss=6545408, pss=6872064, swap=0)
+ >>> p.memory_percent()
+ 0.7823
+ >>> p.memory_maps()
+ [pmmap_grouped(path='/lib/x8664-linux-gnu/libutil-2.15.so', rss=32768, size=2125824, pss=32768, shared_clean=0, shared_dirty=0, private_clean=20480, private_dirty=12288, referenced=32768, anonymous=12288, swap=0),
+ pmmap_grouped(path='/lib/x8664-linux-gnu/libc-2.15.so', rss=3821568, size=3842048, pss=3821568, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=3821568, referenced=3575808, anonymous=3821568, swap=0),
+ pmmap_grouped(path='[heap]', rss=32768, size=139264, pss=32768, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=32768, referenced=32768, anonymous=32768, swap=0),
+ pmmap_grouped(path='[stack]', rss=2465792, size=2494464, pss=2465792, shared_clean=0, shared_dirty=0, private_clean=0, private_dirty=2465792, referenced=2277376, anonymous=2465792, swap=0),
+ ...]
+ >>>
+ >>> p.io_counters()
+ pio(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632, read_chars=456232, write_chars=517543)
+ >>>
+ >>> p.open_files()
+ [popenfile(path='/home/giampaolo/monit.py', fd=3, position=0, mode='r', flags=32768),
+ popenfile(path='/var/log/monit.log', fd=4, position=235542, mode='a', flags=33793)]
+ >>>
+ >>> p.connections(kind='tcp')
+ [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_ctx_switches()
+ pctxsw(voluntary=78, involuntary=19)
+ >>>
+ >>> p.nice()
+ 0
+ >>> p.nice(10) # set
+ >>>
+ >>> p.ionice(psutil.IOPRIO_CLASS_IDLE) # IO priority (Win and Linux only)
+ >>> p.ionice()
+ pionice(ioclass=<IOPriority.IOPRIO_CLASS_IDLE: 3>, value=0)
+ >>>
+ >>> p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) # set resource limits (Linux only)
+ >>> p.rlimit(psutil.RLIMIT_NOFILE)
+ (5, 5)
+ >>>
+ >>> p.environ()
+ {'LC_PAPER': 'it_IT.UTF-8', 'SHELL': '/bin/bash', 'GREP_OPTIONS': '--color=auto',
+ 'XDG_CONFIG_DIRS': '/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg',
+ ...}
+ >>>
+ >>> p.as_dict()
+ {'status': 'running', 'num_ctx_switches': pctxsw(voluntary=63, involuntary=1), 'pid': 5457, ...}
+ >>> p.is_running()
+ True
+ >>> p.suspend()
+ >>> p.resume()
+ >>>
+ >>> p.terminate()
+ >>> p.kill()
+ >>> p.wait(timeout=3)
+ <Exitcode.EX_OK: 0>
+ >>>
+ >>> psutil.test()
+ USER PID %CPU %MEM VSZ RSS TTY START TIME COMMAND
+ root 1 0.0 0.0 24584 2240 Jun17 00:00 init
+ root 2 0.0 0.0 0 0 Jun17 00:00 kthreadd
+ ...
+ giampaolo 31475 0.0 0.0 20760 3024 /dev/pts/0 Jun19 00:00 python2.4
+ giampaolo 31721 0.0 2.2 773060 181896 00:04 10:30 chrome
+ root 31763 0.0 0.0 0 0 00:05 00:00 kworker/0:1
+ >>>
+
+Further process APIs
+--------------------
+
+.. code-block:: python
+
+ >>> import psutil
+ >>> for proc in psutil.process_iter(['pid', 'name']):
+ ... print(proc.info)
+ ...
+ {'pid': 1, 'name': 'systemd'}
+ {'pid': 2, 'name': 'kthreadd'}
+ {'pid': 3, 'name': 'ksoftirqd/0'}
+ ...
+ >>>
+ >>> psutil.pid_exists(3)
+ True
+ >>>
+ >>> def on_terminate(proc):
+ ... print("process {} terminated".format(proc))
+ ...
+ >>> # waits for multiple processes to terminate
+ >>> 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
+----------------
+
+.. code-block:: python
+
+ >>> list(psutil.win_service_iter())
+ [<WindowsService(name='AeLookupSvc', display_name='Application Experience') at 38850096>,
+ <WindowsService(name='ALG', display_name='Application Layer Gateway Service') at 38850128>,
+ <WindowsService(name='APNMCP', display_name='Ask Update Service') at 38850160>,
+ <WindowsService(name='AppIDSvc', display_name='Application Identity') at 38850192>,
+ ...]
+ >>> s = psutil.win_service_get('alg')
+ >>> s.as_dict()
+ {'binpath': 'C:\\Windows\\System32\\alg.exe',
+ 'description': 'Provides support for 3rd party protocol plug-ins for Internet Connection Sharing',
+ 'display_name': 'Application Layer Gateway Service',
+ 'name': 'alg',
+ 'pid': None,
+ 'start_type': 'manual',
+ 'status': 'stopped',
+ 'username': 'NT AUTHORITY\\LocalService'}
+
+Projects using psutil
+=====================
+
+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/Jahaja/psdash
+- https://github.com/ajenti/ajenti
+- https://github.com/home-assistant/home-assistant/
+
+Portings
+========
+
+- Go: https://github.com/shirou/gopsutil
+- 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
new file mode 100644
index 0000000000..acd42ac264
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/__init__.py
@@ -0,0 +1,2407 @@
+# -*- coding: utf-8 -*-
+
+# 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.
+
+"""psutil is a cross-platform library for retrieving information on
+running processes and system utilization (CPU, memory, disks, network,
+sensors) in Python. Supported platforms:
+
+ - Linux
+ - Windows
+ - macOS
+ - FreeBSD
+ - OpenBSD
+ - NetBSD
+ - Sun Solaris
+ - AIX
+
+Works with Python versions from 2.6 to 3.4+.
+"""
+
+from __future__ import division
+import collections
+import contextlib
+import datetime
+import functools
+import os
+import signal
+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 CONN_CLOSE
+from ._common import CONN_CLOSE_WAIT
+from ._common import CONN_CLOSING
+from ._common import CONN_ESTABLISHED
+from ._common import CONN_FIN_WAIT1
+from ._common import CONN_FIN_WAIT2
+from ._common import CONN_LAST_ACK
+from ._common import CONN_LISTEN
+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 NIC_DUPLEX_FULL
+from ._common import NIC_DUPLEX_HALF
+from ._common import NIC_DUPLEX_UNKNOWN
+from ._common import POWER_TIME_UNKNOWN
+from ._common import POWER_TIME_UNLIMITED
+from ._common import STATUS_DEAD
+from ._common import STATUS_DISK_SLEEP
+from ._common import STATUS_IDLE
+from ._common import STATUS_LOCKED
+from ._common import STATUS_PARKED
+from ._common import STATUS_RUNNING
+from ._common import STATUS_SLEEPING
+from ._common import STATUS_STOPPED
+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
+
+if LINUX:
+ # This is public API and it will be retrieved from _pslinux.py
+ # via sys.modules.
+ 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
+ from ._pslinux import IOPRIO_CLASS_RT # NOQA
+
+elif WINDOWS:
+ from . import _pswindows as _psplatform
+ from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS # NOQA
+ from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS # NOQA
+ from ._psutil_windows import HIGH_PRIORITY_CLASS # NOQA
+ from ._psutil_windows import IDLE_PRIORITY_CLASS # NOQA
+ 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_LOW # NOQA
+ from ._pswindows import IOPRIO_NORMAL # NOQA
+ from ._pswindows import IOPRIO_HIGH # NOQA
+
+elif MACOS:
+ from . import _psosx as _psplatform
+
+elif BSD:
+ from . import _psbsd as _psplatform
+
+elif SUNOS:
+ from . import _pssunos as _psplatform
+ from ._pssunos import CONN_BOUND # NOQA
+ from ._pssunos import CONN_IDLE # NOQA
+
+ # This is public writable API which is read from _pslinux.py and
+ # _pssunos.py via sys.modules.
+ PROCFS_PATH = "/proc"
+
+elif AIX:
+ from . import _psaix as _psplatform
+
+ # This is public API and it will be retrieved from _pslinux.py
+ # via sys.modules.
+ PROCFS_PATH = "/proc"
+
+else: # pragma: no cover
+ raise NotImplementedError('platform %s is not supported' % sys.platform)
+
+
+__all__ = [
+ # exceptions
+ "Error", "NoSuchProcess", "ZombieProcess", "AccessDenied",
+ "TimeoutExpired",
+
+ # constants
+ "version_info", "__version__",
+
+ "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP",
+ "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD",
+ "STATUS_WAKING", "STATUS_LOCKED", "STATUS_WAITING", "STATUS_LOCKED",
+ "STATUS_PARKED",
+
+ "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_NONE",
+ # "CONN_IDLE", "CONN_BOUND",
+
+ "AF_LINK",
+
+ "NIC_DUPLEX_FULL", "NIC_DUPLEX_HALF", "NIC_DUPLEX_UNKNOWN",
+
+ "POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED",
+
+ "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "MACOS", "OSX", "POSIX",
+ "SUNOS", "WINDOWS", "AIX",
+
+ # "RLIM_INFINITY", "RLIMIT_AS", "RLIMIT_CORE", "RLIMIT_CPU", "RLIMIT_DATA",
+ # "RLIMIT_FSIZE", "RLIMIT_LOCKS", "RLIMIT_MEMLOCK", "RLIMIT_NOFILE",
+ # "RLIMIT_NPROC", "RLIMIT_RSS", "RLIMIT_STACK", "RLIMIT_MSGQUEUE",
+ # "RLIMIT_NICE", "RLIMIT_RTPRIO", "RLIMIT_RTTIME", "RLIMIT_SIGPENDING",
+
+ # classes
+ "Process", "Popen",
+
+ # functions
+ "pid_exists", "pids", "process_iter", "wait_procs", # proc
+ "virtual_memory", "swap_memory", # memory
+ "cpu_times", "cpu_percent", "cpu_times_percent", "cpu_count", # cpu
+ "cpu_stats", # "cpu_freq", "getloadavg"
+ "net_io_counters", "net_connections", "net_if_addrs", # network
+ "net_if_stats",
+ "disk_io_counters", "disk_partitions", "disk_usage", # disk
+ # "sensors_temperatures", "sensors_battery", "sensors_fans" # sensors
+ "users", "boot_time", # others
+]
+
+
+__all__.extend(_psplatform.__extra__all__)
+
+# Linux, FreeBSD
+if hasattr(_psplatform.Process, "rlimit"):
+ # Populate global namespace with RLIM* constants.
+ from . import _psutil_posix
+
+ _globals = globals()
+ _name = None
+ for _name in dir(_psutil_posix):
+ if _name.startswith('RLIM') and _name.isupper():
+ _globals[_name] = getattr(_psutil_posix, _name)
+ __all__.append(_name)
+ del _globals, _name
+
+AF_LINK = _psplatform.AF_LINK
+
+__author__ = "Giampaolo Rodola'"
+__version__ = "5.8.0"
+version_info = tuple([int(num) for num in __version__.split('.')])
+
+_timer = getattr(time, 'monotonic', time.time)
+_TOTAL_PHYMEM = None
+_LOWEST_PID = None
+_SENTINEL = object()
+
+# Sanity check in case the user messed up with psutil installation
+# or did something weird with sys.path. In this case we might end
+# up importing a python module using a C extension module which
+# 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 hasattr(_psplatform.cext, 'version'):
+ msg += " (%s instead of %s)" % (
+ '.'.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"))
+ msg += " or clean the virtual env somehow, then reinstall"
+ raise ImportError(msg)
+
+
+# =====================================================================
+# --- Utils
+# =====================================================================
+
+
+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().
+ """
+ ret = {}
+ for pid in pids():
+ try:
+ ret[pid] = _psplatform.Process(pid).ppid()
+ except (NoSuchProcess, ZombieProcess):
+ pass
+ 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"
+ return datetime.datetime.fromtimestamp(secs).strftime(fmt)
+
+
+# =====================================================================
+# --- Process class
+# =====================================================================
+
+
+class Process(object):
+ """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.
+
+ Note that most of the methods of this class do not make sure
+ the PID of the process being queried has been reused over time.
+ That means you might end up retrieving an information referring
+ to another process in case the original one this instance
+ refers to is gone in the meantime.
+
+ The only exceptions for which process identity is pre-emptively
+ checked and guaranteed are:
+
+ - parent()
+ - children()
+ - nice() (set)
+ - ionice() (set)
+ - rlimit() (set)
+ - cpu_affinity (set)
+ - suspend()
+ - resume()
+ - send_signal()
+ - terminate()
+ - kill()
+
+ To prevent this problem for all other methods you can:
+ - 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
+ """
+
+ def __init__(self, pid=None):
+ self._init(pid)
+
+ def _init(self, pid, _ignore_nsp=False):
+ if pid is None:
+ pid = os.getpid()
+ else:
+ if not _PY3 and not isinstance(pid, (int, long)):
+ raise TypeError('pid must be an integer (got %r)' % pid)
+ if pid < 0:
+ raise ValueError('pid must be a positive integer (got %s)'
+ % pid)
+ self._pid = pid
+ self._name = None
+ self._exe = None
+ self._create_time = None
+ self._gone = False
+ self._hash = None
+ self._lock = threading.RLock()
+ # used for caching on Windows only (on POSIX ppid may change)
+ self._ppid = None
+ # platform-specific modules define an _psplatform.Process
+ # implementation class
+ self._proc = _psplatform.Process(pid)
+ self._last_sys_cpu_times = None
+ self._last_proc_cpu_times = None
+ self._exitcode = _SENTINEL
+ # cache creation time for later use in is_running() method
+ try:
+ self.create_time()
+ except AccessDenied:
+ # We should never get here as AFAIK we're able to get
+ # process creation time on all platforms even as a
+ # limited user.
+ pass
+ except ZombieProcess:
+ # Zombies can still be queried by this class (although
+ # not always) and pids() return them so just go on.
+ pass
+ except NoSuchProcess:
+ if not _ignore_nsp:
+ msg = 'no process found with pid %s' % pid
+ raise NoSuchProcess(pid, None, msg)
+ else:
+ self._gone = True
+ # This pair is supposed to indentify 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["pid"] = self.pid
+ if self._name:
+ info['name'] = self._name
+ with self.oneshot():
+ try:
+ info["name"] = self.name()
+ info["status"] = self.status()
+ except ZombieProcess:
+ info["status"] = "zombie"
+ except NoSuchProcess:
+ info["status"] = "terminated"
+ except AccessDenied:
+ pass
+ if self._exitcode not in (_SENTINEL, None):
+ info["exitcode"] = self._exitcode
+ if self._create_time:
+ 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()]))
+
+ __repr__ = __str__
+
+ def __eq__(self, other):
+ # Test for equality with another Process object based
+ # on PID and creation time.
+ if not isinstance(other, Process):
+ return NotImplemented
+ return self._ident == other._ident
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __hash__(self):
+ if self._hash is None:
+ self._hash = hash(self._ident)
+ return self._hash
+
+ @property
+ def pid(self):
+ """The process PID."""
+ return self._pid
+
+ # --- utility methods
+
+ @contextlib.contextmanager
+ def oneshot(self):
+ """Utility context manager which considerably speeds up the
+ retrieval of multiple process information at the same time.
+
+ Internally different process info (e.g. name, ppid, uids,
+ gids, ...) may be fetched by using the same routine, but
+ only one information is returned and the others are discarded.
+ When using this context manager the internal routine is
+ executed once (in the example below on name()) and the
+ other info are cached.
+
+ The cache is cleared when exiting the context manager block.
+ The advice is to use this every time you retrieve more than
+ one information about the process. If you're lucky, you'll
+ get a hell of a speedup.
+
+ >>> import psutil
+ >>> p = psutil.Process()
+ >>> with p.oneshot():
+ ... p.name() # collect multiple info
+ ... p.cpu_times() # return cached value
+ ... p.cpu_percent() # return cached value
+ ... p.create_time() # return cached value
+ ...
+ >>>
+ """
+ with self._lock:
+ if hasattr(self, "_cache"):
+ # NOOP: this covers the use case where the user enters the
+ # context twice:
+ #
+ # >>> with p.oneshot():
+ # ... with p.oneshot():
+ # ...
+ #
+ # Also, since as_dict() internally uses oneshot()
+ # I expect that the code below will be a pretty common
+ # "mistake" that the user will make, so let's guard
+ # against that:
+ #
+ # >>> with p.oneshot():
+ # ... p.as_dict()
+ # ...
+ yield
+ else:
+ try:
+ # cached in case cpu_percent() is used
+ self.cpu_times.cache_activate(self)
+ # cached in case memory_percent() is used
+ self.memory_info.cache_activate(self)
+ # cached in case parent() is used
+ self.ppid.cache_activate(self)
+ # cached in case username() is used
+ if POSIX:
+ self.uids.cache_activate(self)
+ # specific implementation cache
+ self._proc.oneshot_enter()
+ yield
+ finally:
+ self.cpu_times.cache_deactivate(self)
+ self.memory_info.cache_deactivate(self)
+ self.ppid.cache_deactivate(self)
+ if POSIX:
+ self.uids.cache_deactivate(self)
+ self._proc.oneshot_exit()
+
+ def as_dict(self, attrs=None, ad_value=None):
+ """Utility method returning process information as a
+ hashable dictionary.
+ If *attrs* is specified it must be a list of strings
+ reflecting available Process class' attribute names
+ (e.g. ['cpu_times', 'name']) else all public (read
+ only) attributes are assumed.
+ *ad_value* is the value which gets assigned in case
+ AccessDenied or ZombieProcess exception is raised when
+ retrieving that particular process information.
+ """
+ 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))
+ attrs = set(attrs)
+ invalid_names = attrs - valid_names
+ if invalid_names:
+ raise ValueError("invalid attr name%s %s" % (
+ "s" if len(invalid_names) > 1 else "",
+ ", ".join(map(repr, invalid_names))))
+
+ retdict = dict()
+ ls = attrs or valid_names
+ with self.oneshot():
+ for name in ls:
+ try:
+ if name == 'pid':
+ ret = self.pid
+ else:
+ meth = getattr(self, name)
+ ret = meth()
+ except (AccessDenied, ZombieProcess):
+ ret = ad_value
+ except NotImplementedError:
+ # in case of not implemented functionality (may happen
+ # on old or exotic systems) we want to crash only if
+ # the user explicitly asked for that particular attr
+ if attrs:
+ raise
+ continue
+ retdict[name] = ret
+ return retdict
+
+ def parent(self):
+ """Return the parent process as a Process object pre-emptively
+ checking whether PID has been reused.
+ If no parent is known return None.
+ """
+ lowest_pid = _LOWEST_PID if _LOWEST_PID is not None else pids()[0]
+ if self.pid == lowest_pid:
+ return None
+ ppid = self.ppid()
+ if ppid is not None:
+ ctime = self.create_time()
+ try:
+ parent = Process(ppid)
+ if parent.create_time() <= ctime:
+ return parent
+ # ...else ppid has been reused by another process
+ except NoSuchProcess:
+ pass
+
+ def parents(self):
+ """Return the parents of this process as a list of Process
+ instances. If no parents are known return an empty list.
+ """
+ parents = []
+ proc = self.parent()
+ while proc is not None:
+ parents.append(proc)
+ proc = proc.parent()
+ return parents
+
+ def is_running(self):
+ """Return whether this process is running.
+ It also checks if PID has been reused by another process in
+ which case return False.
+ """
+ if self._gone:
+ return False
+ try:
+ # Checking if PID is alive is not enough as the PID might
+ # have been reused by another process: we also want to
+ # 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)
+ except ZombieProcess:
+ # We should never get here as it's already handled in
+ # Process.__init__; here just for extra safety.
+ return True
+ except NoSuchProcess:
+ self._gone = True
+ return False
+
+ # --- actual API
+
+ @memoize_when_activated
+ def ppid(self):
+ """The process parent PID.
+ On Windows the return value is cached after first call.
+ """
+ # On POSIX we don't want to cache the ppid as it may unexpectedly
+ # change to 1 (init) in case this process turns into a zombie:
+ # https://github.com/giampaolo/psutil/issues/321
+ # http://stackoverflow.com/questions/356722/
+
+ # XXX should we check creation time here rather than in
+ # Process.parent()?
+ if POSIX:
+ return self._proc.ppid()
+ else: # pragma: no cover
+ self._ppid = self._ppid or self._proc.ppid()
+ return self._ppid
+
+ def name(self):
+ """The process name. The return value is cached after first call."""
+ # Process name is only cached on Windows as on POSIX it may
+ # change, see:
+ # https://github.com/giampaolo/psutil/issues/692
+ if WINDOWS and self._name is not None:
+ return self._name
+ name = self._proc.name()
+ if POSIX and len(name) >= 15:
+ # On UNIX the name gets truncated to the first 15 characters.
+ # If it matches the first part of the cmdline we return that
+ # one instead because it's usually more explicative.
+ # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon".
+ try:
+ cmdline = self.cmdline()
+ except AccessDenied:
+ pass
+ else:
+ if cmdline:
+ extended_name = os.path.basename(cmdline[0])
+ if extended_name.startswith(name):
+ name = extended_name
+ self._name = name
+ self._proc._name = name
+ return name
+
+ def exe(self):
+ """The process executable as an absolute path.
+ 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
+ cmdline = self.cmdline()
+ if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'):
+ exe = cmdline[0] # the possible exe
+ # 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)):
+ return exe
+ if isinstance(fallback, AccessDenied):
+ raise fallback
+ return fallback
+
+ if self._exe is None:
+ try:
+ exe = self._proc.exe()
+ except AccessDenied as err:
+ return guess_it(fallback=err)
+ else:
+ if not exe:
+ # underlying implementation can legitimately return an
+ # empty string; if that's the case we don't want to
+ # raise AD while guessing from the cmdline
+ try:
+ exe = guess_it(fallback=exe)
+ except AccessDenied:
+ pass
+ self._exe = exe
+ return self._exe
+
+ def cmdline(self):
+ """The command line this process has been called with."""
+ return self._proc.cmdline()
+
+ def status(self):
+ """The process current status as a STATUS_* constant."""
+ try:
+ return self._proc.status()
+ except ZombieProcess:
+ return STATUS_ZOMBIE
+
+ def username(self):
+ """The name of the user that owns the process.
+ On UNIX this is calculated by using *real* process uid.
+ """
+ if POSIX:
+ if pwd is None:
+ # might happen if python was installed from sources
+ raise ImportError(
+ "requires pwd module shipped with standard python")
+ real_uid = self.uids().real
+ try:
+ return pwd.getpwuid(real_uid).pw_name
+ except KeyError:
+ # the uid can't be resolved by the system
+ return str(real_uid)
+ else:
+ return self._proc.username()
+
+ def create_time(self):
+ """The process creation time as a floating point number
+ expressed in seconds since the epoch.
+ The return value is cached after first call.
+ """
+ if self._create_time is None:
+ self._create_time = self._proc.create_time()
+ return self._create_time
+
+ def cwd(self):
+ """Process current working directory as an absolute path."""
+ return self._proc.cwd()
+
+ def nice(self, value=None):
+ """Get or set process niceness (priority)."""
+ if value is None:
+ return self._proc.nice_get()
+ else:
+ if not self.is_running():
+ raise NoSuchProcess(self.pid, self._name)
+ self._proc.nice_set(value)
+
+ if POSIX:
+
+ @memoize_when_activated
+ def uids(self):
+ """Return process UIDs as a (real, effective, saved)
+ namedtuple.
+ """
+ return self._proc.uids()
+
+ def gids(self):
+ """Return process GIDs as a (real, effective, saved)
+ namedtuple.
+ """
+ return self._proc.gids()
+
+ def terminal(self):
+ """The terminal associated with this process, if any,
+ else None.
+ """
+ return self._proc.terminal()
+
+ def num_fds(self):
+ """Return the number of file descriptors opened by this
+ process (POSIX only).
+ """
+ return self._proc.num_fds()
+
+ # Linux, BSD, AIX and Windows only
+ if hasattr(_psplatform.Process, "io_counters"):
+
+ def io_counters(self):
+ """Return process I/O statistics as a
+ (read_count, write_count, read_bytes, write_bytes)
+ namedtuple.
+ Those are the number of read/write calls performed and the
+ amount of bytes read and written by the process.
+ """
+ return self._proc.io_counters()
+
+ # Linux and Windows
+ if hasattr(_psplatform.Process, "ionice_get"):
+
+ def ionice(self, ioclass=None, value=None):
+ """Get or set process I/O niceness (priority).
+
+ On Linux *ioclass* is one of the IOPRIO_CLASS_* constants.
+ *value* is a number which goes from 0 to 7. The higher the
+ value, the lower the I/O priority of the process.
+
+ On Windows only *ioclass* is used and it can be set to 2
+ (normal), 1 (low) or 0 (very low).
+
+ Available on Linux and Windows > Vista only.
+ """
+ if ioclass is None:
+ if value is not None:
+ raise ValueError("'ioclass' argument must be specified")
+ return self._proc.ionice_get()
+ else:
+ return self._proc.ionice_set(ioclass, value)
+
+ # Linux / FreeBSD only
+ if hasattr(_psplatform.Process, "rlimit"):
+
+ def rlimit(self, resource, limits=None):
+ """Get or set process resource limits as a (soft, hard)
+ tuple.
+
+ *resource* is one of the RLIMIT_* constants.
+ *limits* is supposed to be a (soft, hard) tuple.
+
+ See "man prlimit" for further info.
+ Available on Linux and FreeBSD only.
+ """
+ return self._proc.rlimit(resource, limits)
+
+ # Windows, Linux and FreeBSD only
+ if hasattr(_psplatform.Process, "cpu_affinity_get"):
+
+ def cpu_affinity(self, cpus=None):
+ """Get or set process CPU affinity.
+ If specified, *cpus* must be a list of CPUs for which you
+ want to set the affinity (e.g. [0, 1]).
+ If an empty list is passed, all egible CPUs are assumed
+ (and set).
+ (Windows, Linux and BSD only).
+ """
+ if cpus is None:
+ return sorted(set(self._proc.cpu_affinity_get()))
+ else:
+ if not cpus:
+ if hasattr(self._proc, "_get_eligible_cpus"):
+ cpus = self._proc._get_eligible_cpus()
+ else:
+ cpus = tuple(range(len(cpu_times(percpu=True))))
+ self._proc.cpu_affinity_set(list(set(cpus)))
+
+ # Linux, FreeBSD, SunOS
+ if hasattr(_psplatform.Process, "cpu_num"):
+
+ def cpu_num(self):
+ """Return what CPU this process is currently running on.
+ The returned number should be <= psutil.cpu_count()
+ and <= len(psutil.cpu_percent(percpu=True)).
+ It may be used in conjunction with
+ psutil.cpu_percent(percpu=True) to observe the system
+ workload distributed across CPUs.
+ """
+ return self._proc.cpu_num()
+
+ # All platforms has it, but maybe not in the future.
+ if hasattr(_psplatform.Process, "environ"):
+
+ def environ(self):
+ """The environment variables of the process as a dict. Note: this
+ might not reflect changes made after the process started. """
+ return self._proc.environ()
+
+ if WINDOWS:
+
+ def num_handles(self):
+ """Return the number of handles opened by this process
+ (Windows only).
+ """
+ return self._proc.num_handles()
+
+ def num_ctx_switches(self):
+ """Return the number of voluntary and involuntary context
+ switches performed by this process.
+ """
+ return self._proc.num_ctx_switches()
+
+ def num_threads(self):
+ """Return the number of threads used by this process."""
+ return self._proc.num_threads()
+
+ if hasattr(_psplatform.Process, "threads"):
+
+ def threads(self):
+ """Return threads opened by process as a list of
+ (id, user_time, system_time) namedtuples representing
+ thread id and thread CPU times (user/system).
+ On OpenBSD this method requires root access.
+ """
+ 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.
+ If *recursive* is True return all the parent descendants.
+
+ Example (A == this process):
+
+ A ─┐
+ │
+ ├─ B (child) ─┐
+ │ └─ X (grandchild) ─┐
+ │ └─ Y (great grandchild)
+ ├─ C (child)
+ └─ D (child)
+
+ >>> import psutil
+ >>> p = psutil.Process()
+ >>> p.children()
+ B, C, D
+ >>> p.children(recursive=True)
+ B, X, Y, C, D
+
+ Note that in the example above if process X disappears
+ process Y won't be listed as the reference to process A
+ is lost.
+ """
+ ppid_map = _ppid_map()
+ ret = []
+ if not recursive:
+ for pid, ppid in ppid_map.items():
+ if ppid == self.pid:
+ try:
+ child = Process(pid)
+ # if child happens to be older than its parent
+ # (self) it means child's PID has been reused
+ if self.create_time() <= child.create_time():
+ ret.append(child)
+ except (NoSuchProcess, ZombieProcess):
+ pass
+ else:
+ # Construct a {pid: [child pids]} dict
+ reverse_ppid_map = collections.defaultdict(list)
+ for pid, ppid in ppid_map.items():
+ reverse_ppid_map[ppid].append(pid)
+ # Recursively traverse that dict, starting from self.pid,
+ # such that we only call Process() on actual children
+ seen = set()
+ stack = [self.pid]
+ while stack:
+ pid = stack.pop()
+ if pid in seen:
+ # Since pids can be reused while the ppid_map is
+ # constructed, there may be rare instances where
+ # there's a cycle in the recorded process "tree".
+ continue
+ seen.add(pid)
+ for child_pid in reverse_ppid_map[pid]:
+ try:
+ child = Process(child_pid)
+ # if child happens to be older than its parent
+ # (self) it means child's PID has been reused
+ intime = self.create_time() <= child.create_time()
+ if intime:
+ ret.append(child)
+ stack.append(child_pid)
+ except (NoSuchProcess, ZombieProcess):
+ pass
+ return ret
+
+ def cpu_percent(self, interval=None):
+ """Return a float representing the current process CPU
+ utilization as a percentage.
+
+ When *interval* is 0.0 or None (default) compares process times
+ to system CPU times elapsed since last call, returning
+ immediately (non-blocking). That means that the first time
+ this is called it will return a meaningful 0.0 value.
+
+ When *interval* is > 0.0 compares process times to system CPU
+ times elapsed before and after the interval (blocking).
+
+ In this case is recommended for accuracy that this function
+ be called with at least 0.1 seconds between calls.
+
+ A value > 100.0 can be returned in case of processes running
+ multiple threads on different CPU cores.
+
+ The returned value is explicitly NOT split evenly between
+ all available logical CPUs. This means that a busy loop process
+ running on a system with 2 logical CPUs will be reported as
+ having 100% CPU utilization instead of 50%.
+
+ Examples:
+
+ >>> import psutil
+ >>> p = psutil.Process(os.getpid())
+ >>> # blocking
+ >>> p.cpu_percent(interval=1)
+ 2.0
+ >>> # non-blocking (percentage since last call)
+ >>> p.cpu_percent(interval=None)
+ 2.9
+ >>>
+ """
+ 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)
+ num_cpus = cpu_count() or 1
+
+ def timer():
+ return _timer() * num_cpus
+
+ if blocking:
+ st1 = timer()
+ pt1 = self._proc.cpu_times()
+ time.sleep(interval)
+ st2 = timer()
+ pt2 = self._proc.cpu_times()
+ else:
+ st1 = self._last_sys_cpu_times
+ pt1 = self._last_proc_cpu_times
+ st2 = timer()
+ pt2 = self._proc.cpu_times()
+ if st1 is None or pt1 is None:
+ self._last_sys_cpu_times = st2
+ self._last_proc_cpu_times = pt2
+ return 0.0
+
+ delta_proc = (pt2.user - pt1.user) + (pt2.system - pt1.system)
+ delta_time = st2 - st1
+ # reset values for next call in case of interval == None
+ self._last_sys_cpu_times = st2
+ self._last_proc_cpu_times = pt2
+
+ try:
+ # 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)
+ except ZeroDivisionError:
+ # interval was too low
+ return 0.0
+ else:
+ # Note 1:
+ # in order to emulate "top" we multiply the value for the num
+ # of CPU cores. This way the busy process will be reported as
+ # having 100% (or more) usage.
+ #
+ # Note 2:
+ # taskmgr.exe on Windows differs in that it will show 50%
+ # instead.
+ #
+ # Note 3:
+ # a percentage > 100 is legitimate as it can result from a
+ # process with multiple threads running on different CPU
+ # cores (top does the same), see:
+ # http://stackoverflow.com/questions/1032357
+ # https://github.com/giampaolo/psutil/issues/474
+ single_cpu_percent = overall_cpus_percent * num_cpus
+ return round(single_cpu_percent, 1)
+
+ @memoize_when_activated
+ def cpu_times(self):
+ """Return a (user, system, children_user, children_system)
+ namedtuple representing the accumulated process time, in
+ seconds.
+ This is similar to os.times() but per-process.
+ On macOS and Windows children_user and children_system are
+ always set to 0.
+ """
+ return self._proc.cpu_times()
+
+ @memoize_when_activated
+ def memory_info(self):
+ """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`.
+
+ All numbers are expressed in bytes.
+ """
+ return self._proc.memory_info()
+
+ @_common.deprecated_method(replacement="memory_info")
+ def memory_info_ex(self):
+ return self.memory_info()
+
+ def memory_full_info(self):
+ """This method returns the same information as memory_info(),
+ plus, on some platform (Linux, macOS, Windows), also provides
+ additional metrics (USS, PSS and swap).
+ The additional metrics provide a better representation of actual
+ process memory usage.
+
+ Namely USS is the memory which is unique to a process and which
+ would be freed if the process was terminated right now.
+
+ It does so by passing through the whole process address.
+ As such it usually requires higher user privileges than
+ memory_info() and is considerably slower.
+ """
+ return self._proc.memory_full_info()
+
+ def memory_percent(self, memtype="rss"):
+ """Compare process memory to total physical system memory and
+ calculate process memory utilization as a percentage.
+ *memtype* argument is a string that dictates what type of
+ process memory you want to compare against (defaults to "rss").
+ The list of available strings can be obtained like this:
+
+ >>> psutil.Process().memory_info()._fields
+ ('rss', 'vms', 'shared', 'text', 'lib', 'data', 'dirty', 'uss', 'pss')
+ """
+ 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
+ metrics = fun()
+ value = getattr(metrics, memtype)
+
+ # use cached value if available
+ 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)
+ 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.
+
+ If *grouped* is True the mapped regions with the same 'path'
+ are grouped together and the different memory fields are summed.
+
+ If *grouped* is False every mapped region is shown as a single
+ entity and the namedtuple will also include the mapped region's
+ address space ('addr') and permission set ('perms').
+ """
+ it = self._proc.memory_maps()
+ if grouped:
+ d = {}
+ for tupl in it:
+ path = tupl[2]
+ nums = tupl[3:]
+ try:
+ d[path] = map(lambda x, y: x + y, d[path], nums)
+ except KeyError:
+ d[path] = nums
+ nt = _psplatform.pmmap_grouped
+ return [nt(path, *d[path]) for path in d] # NOQA
+ else:
+ nt = _psplatform.pmmap_ext
+ return [nt(*x) for x in it]
+
+ def open_files(self):
+ """Return files opened by process as a list of
+ (path, fd) namedtuples including the absolute file name
+ and file descriptor number.
+ """
+ return self._proc.open_files()
+
+ def connections(self, kind='inet'):
+ """Return socket connections opened by process as a list of
+ (fd, family, type, laddr, raddr, status) namedtuples.
+ The *kind* parameter filters for connections that match the
+ following criteria:
+
+ +------------+----------------------------------------------------+
+ | Kind Value | Connections using |
+ +------------+----------------------------------------------------+
+ | inet | IPv4 and IPv6 |
+ | inet4 | IPv4 |
+ | inet6 | IPv6 |
+ | tcp | TCP |
+ | tcp4 | TCP over IPv4 |
+ | tcp6 | TCP over IPv6 |
+ | udp | UDP |
+ | udp4 | UDP over IPv4 |
+ | udp6 | UDP over IPv6 |
+ | unix | UNIX socket (both UDP and TCP protocols) |
+ | all | the sum of all the possible families and protocols |
+ +------------+----------------------------------------------------+
+ """
+ return self._proc.connections(kind)
+
+ # --- signals
+
+ if POSIX:
+ def _send_signal(self, sig):
+ assert not self.pid < 0, self.pid
+ if self.pid == 0:
+ # see "man 2 kill"
+ raise ValueError(
+ "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")
+ try:
+ os.kill(self.pid, sig)
+ except ProcessLookupError:
+ if OPENBSD and pid_exists(self.pid):
+ # We do this because os.kill() lies in case of
+ # zombie processes.
+ raise ZombieProcess(self.pid, self._name, self._ppid)
+ else:
+ self._gone = True
+ raise NoSuchProcess(self.pid, self._name)
+ 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) .
+ On Windows only SIGTERM is valid and is treated as an alias
+ for kill().
+ """
+ if POSIX:
+ self._send_signal(sig)
+ else: # pragma: no cover
+ 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.
+ """
+ if POSIX:
+ self._send_signal(signal.SIGSTOP)
+ else: # pragma: no cover
+ self._proc.suspend()
+
+ @_assert_pid_not_reused
+ def resume(self):
+ """Resume process execution with SIGCONT pre-emptively checking
+ whether PID has been reused.
+ On Windows this has the effect of resuming all process threads.
+ """
+ if POSIX:
+ self._send_signal(signal.SIGCONT)
+ else: # pragma: no cover
+ self._proc.resume()
+
+ @_assert_pid_not_reused
+ def terminate(self):
+ """Terminate the process with SIGTERM pre-emptively checking
+ whether PID has been reused.
+ On Windows this is an alias for kill().
+ """
+ if POSIX:
+ self._send_signal(signal.SIGTERM)
+ else: # pragma: no cover
+ self._proc.kill()
+
+ @_assert_pid_not_reused
+ def kill(self):
+ """Kill the current process with SIGKILL pre-emptively checking
+ whether PID has been reused.
+ """
+ if POSIX:
+ self._send_signal(signal.SIGKILL)
+ else: # pragma: no cover
+ self._proc.kill()
+
+ def wait(self, timeout=None):
+ """Wait for process to terminate and, if process is a children
+ of os.getpid(), also return its exit code, else None.
+ On Windows there's no such limitation (exit code is always
+ returned).
+
+ If the process is already terminated immediately return None
+ instead of raising NoSuchProcess.
+
+ If *timeout* (in seconds) is specified and process is still
+ alive raise TimeoutExpired.
+
+ 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")
+ if self._exitcode is not _SENTINEL:
+ return self._exitcode
+ self._exitcode = self._proc.wait(timeout)
+ return self._exitcode
+
+
+# The valid attr names which can be processed by Process.as_dict().
+_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',
+ 'is_running', 'as_dict', 'parent', 'parents', 'children', 'rlimit',
+ 'memory_info_ex', 'oneshot']])
+
+
+# =====================================================================
+# --- Popen class
+# =====================================================================
+
+
+class Popen(Process):
+ """Same as subprocess.Popen, but in addition it provides all
+ psutil.Process methods in a single class.
+ For the following methods which are common to both classes, psutil
+ implementation takes precedence:
+
+ * send_signal()
+ * terminate()
+ * kill()
+
+ This is done in order to avoid killing another process in case its
+ PID has been reused, fixing BPO-6973.
+
+ >>> import psutil
+ >>> from subprocess import PIPE
+ >>> p = psutil.Popen(["python", "-c", "print 'hi'"], stdout=PIPE)
+ >>> p.name()
+ 'python'
+ >>> p.uids()
+ user(real=1000, effective=1000, saved=1000)
+ >>> p.username()
+ 'giampaolo'
+ >>> p.communicate()
+ ('hi\n', None)
+ >>> p.terminate()
+ >>> p.wait(timeout=2)
+ 0
+ >>>
+ """
+
+ def __init__(self, *args, **kwargs):
+ # Explicitly avoid to raise NoSuchProcess in case the process
+ # spawned by subprocess.Popen terminates too quickly, see:
+ # https://github.com/giampaolo/psutil/issues/193
+ self.__subproc = subprocess.Popen(*args, **kwargs)
+ self._init(self.__subproc.pid, _ignore_nsp=True)
+
+ def __dir__(self):
+ return sorted(set(dir(Popen) + dir(subprocess.Popen)))
+
+ def __enter__(self):
+ if hasattr(self.__subproc, '__enter__'):
+ self.__subproc.__enter__()
+ return self
+
+ def __exit__(self, *args, **kwargs):
+ if hasattr(self.__subproc, '__exit__'):
+ return self.__subproc.__exit__(*args, **kwargs)
+ else:
+ if self.stdout:
+ self.stdout.close()
+ if self.stderr:
+ self.stderr.close()
+ try:
+ # Flushing a BufferedWriter may raise an error.
+ if self.stdin:
+ self.stdin.close()
+ finally:
+ # Wait for the process to terminate, to avoid zombies.
+ self.wait()
+
+ def __getattribute__(self, name):
+ try:
+ return object.__getattribute__(self, name)
+ except AttributeError:
+ try:
+ return object.__getattribute__(self.__subproc, name)
+ except AttributeError:
+ raise AttributeError("%s instance has no attribute '%s'"
+ % (self.__class__.__name__, name))
+
+ def wait(self, timeout=None):
+ if self.__subproc.returncode is not None:
+ return self.__subproc.returncode
+ ret = super(Popen, self).wait(timeout)
+ self.__subproc.returncode = ret
+ return ret
+
+
+# =====================================================================
+# --- system processes related functions
+# =====================================================================
+
+
+def pids():
+ """Return a list of current running PIDs."""
+ global _LOWEST_PID
+ ret = sorted(_psplatform.pids())
+ _LOWEST_PID = ret[0]
+ return ret
+
+
+def pid_exists(pid):
+ """Return True if given PID exists in the current process list.
+ This is faster than doing "pid in psutil.pids()" and
+ should be preferred.
+ """
+ if pid < 0:
+ return False
+ elif pid == 0 and POSIX:
+ # On POSIX we use os.kill() to determine PID existence.
+ # According to "man 2 kill" PID 0 has a special meaning
+ # though: it refers to <<every process in the process
+ # group of the calling process>> and that is not we want
+ # to do here.
+ return pid in pids()
+ else:
+ return _psplatform.pid_exists(pid)
+
+
+_pmap = {}
+_lock = threading.Lock()
+
+
+def process_iter(attrs=None, ad_value=None):
+ """Return a generator yielding a Process instance for all
+ running processes.
+
+ Every new Process instance is only created once and then cached
+ into an internal table which is updated every time this is used.
+
+ Cached Process instances are checked for identity so that you're
+ safe in case a PID has been reused by another process, in which
+ case the cached instance is updated.
+
+ The sorting order in which processes are yielded is based on
+ their PIDs.
+
+ *attrs* and *ad_value* have the same meaning as in
+ Process.as_dict(). If *attrs* is specified as_dict() is called
+ and the resulting dict is stored as a 'info' attribute attached
+ to returned Process instance.
+ If *attrs* is an empty list it will retrieve all process info
+ (slow).
+ """
+ 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
+ return proc
+
+ def remove(pid):
+ with _lock:
+ _pmap.pop(pid, None)
+
+ a = set(pids())
+ 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:
+ 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
+
+
+def wait_procs(procs, timeout=None, callback=None):
+ """Convenience function which waits for a list of processes to
+ terminate.
+
+ Return a (gone, alive) tuple indicating which processes
+ are gone and which ones are still alive.
+
+ The gone ones will have a new *returncode* attribute indicating
+ process exit status (may be None).
+
+ *callback* is a function which gets called every time a process
+ terminates (a Process instance is passed as callback argument).
+
+ Function will return as soon as all processes terminate or when
+ *timeout* occurs.
+ Differently from Process.wait() it will not raise TimeoutExpired if
+ *timeout* occurs.
+
+ Typical use case is:
+
+ - send SIGTERM to a list of processes
+ - give them some time to terminate
+ - send SIGKILL to those ones which are still alive
+
+ Example:
+
+ >>> def on_terminate(proc):
+ ... print("process {} terminated".format(proc))
+ ...
+ >>> for p in procs:
+ ... p.terminate()
+ ...
+ >>> gone, alive = wait_procs(procs, timeout=3, callback=on_terminate)
+ >>> for p in alive:
+ ... p.kill()
+ """
+ def check_gone(proc, timeout):
+ try:
+ returncode = proc.wait(timeout=timeout)
+ except TimeoutExpired:
+ pass
+ else:
+ if returncode is not None or not proc.is_running():
+ # Set new Process instance attribute.
+ proc.returncode = returncode
+ gone.add(proc)
+ if callback is not None:
+ callback(proc)
+
+ if timeout is not None and not timeout >= 0:
+ msg = "timeout must be a positive integer, got %s" % timeout
+ raise ValueError(msg)
+ gone = set()
+ alive = set(procs)
+ if callback is not None and not callable(callback):
+ raise TypeError("callback %r is not a callable" % callable)
+ if timeout is not None:
+ deadline = _timer() + timeout
+
+ while alive:
+ if timeout is not None and timeout <= 0:
+ break
+ for proc in alive:
+ # Make sure that every complete iteration (all processes)
+ # will last max 1 sec.
+ # We do this because we don't want to wait too long on a
+ # single process: in case it terminates too late other
+ # processes may disappear in the meantime and their PID
+ # reused.
+ max_timeout = 1.0 / len(alive)
+ if timeout is not None:
+ timeout = min((deadline - _timer()), max_timeout)
+ if timeout <= 0:
+ break
+ check_gone(proc, timeout)
+ else:
+ check_gone(proc, max_timeout)
+ alive = alive - gone
+
+ if alive:
+ # Last attempt over processes survived so far.
+ # timeout == 0 won't make this function wait any further.
+ for proc in alive:
+ check_gone(proc, 0)
+ alive = alive - gone
+
+ return (list(gone), list(alive))
+
+
+# =====================================================================
+# --- CPU related functions
+# =====================================================================
+
+
+def cpu_count(logical=True):
+ """Return the number of logical CPUs in the system (same as
+ os.cpu_count() in Python 3.4).
+
+ If *logical* is False return the number of physical cores only
+ (e.g. hyper thread CPUs are excluded).
+
+ Return None if undetermined.
+
+ The return value is cached after first call.
+ If desired cache can be cleared like this:
+
+ >>> psutil.cpu_count.cache_clear()
+ """
+ if logical:
+ ret = _psplatform.cpu_count_logical()
+ else:
+ ret = _psplatform.cpu_count_physical()
+ if ret is not None and ret < 1:
+ ret = None
+ return ret
+
+
+def cpu_times(percpu=False):
+ """Return system-wide CPU times as a namedtuple.
+ Every CPU time represents the seconds the CPU has spent in the
+ given mode. The namedtuple's fields availability varies depending on the
+ platform:
+
+ - user
+ - system
+ - idle
+ - nice (UNIX)
+ - iowait (Linux)
+ - irq (Linux, FreeBSD)
+ - softirq (Linux)
+ - steal (Linux >= 2.6.11)
+ - guest (Linux >= 2.6.24)
+ - guest_nice (Linux >= 3.2.0)
+
+ When *percpu* is True return a list of namedtuples for each CPU.
+ First element of the list refers to first CPU, second element
+ to second CPU and so on.
+ The order of the list is consistent across calls.
+ """
+ if not percpu:
+ return _psplatform.cpu_times()
+ else:
+ return _psplatform.per_cpu_times()
+
+
+try:
+ _last_cpu_times = cpu_times()
+except Exception:
+ # Don't want to crash at import time.
+ _last_cpu_times = None
+
+try:
+ _last_per_cpu_times = cpu_times(percpu=True)
+except Exception:
+ # Don't want to crash at import time.
+ _last_per_cpu_times = None
+
+
+def _cpu_tot_time(times):
+ """Given a cpu_time() ntuple calculates the total CPU time
+ (including idle time).
+ """
+ tot = sum(times)
+ if LINUX:
+ # On Linux guest times are already accounted in "user" or
+ # "nice" times, so we subtract them from total.
+ # Htop does the same. References:
+ # https://github.com/giampaolo/psutil/pull/940
+ # http://unix.stackexchange.com/questions/178045
+ # https://github.com/torvalds/linux/blob/
+ # 447976ef4fd09b1be88b316d1a81553f1aa7cd07/kernel/sched/
+ # cputime.c#L158
+ tot -= getattr(times, "guest", 0) # Linux 2.6.24+
+ tot -= getattr(times, "guest_nice", 0) # Linux 3.2.0+
+ return tot
+
+
+def _cpu_busy_time(times):
+ """Given a cpu_time() ntuple calculates the busy CPU time.
+ We do so by subtracting all idle CPU times.
+ """
+ busy = _cpu_tot_time(times)
+ busy -= times.idle
+ # Linux: "iowait" is time during which the CPU does not do anything
+ # (waits for IO to complete). On Linux IO wait is *not* accounted
+ # in "idle" time so we subtract it. Htop does the same.
+ # References:
+ # https://github.com/torvalds/linux/blob/
+ # 447976ef4fd09b1be88b316d1a81553f1aa7cd07/kernel/sched/cputime.c#L244
+ busy -= getattr(times, "iowait", 0)
+ return busy
+
+
+def _cpu_times_deltas(t1, t2):
+ assert t1._fields == t2._fields, (t1, t2)
+ field_deltas = []
+ for field in _psplatform.scputimes._fields:
+ field_delta = getattr(t2, field) - getattr(t1, field)
+ # CPU times are always supposed to increase over time
+ # or at least remain the same and that's because time
+ # cannot go backwards.
+ # Surprisingly sometimes this might not be the case (at
+ # least on Windows and Linux), see:
+ # https://github.com/giampaolo/psutil/issues/392
+ # https://github.com/giampaolo/psutil/issues/645
+ # https://github.com/giampaolo/psutil/issues/1210
+ # Trim negative deltas to zero to ignore decreasing fields.
+ # top does the same. Reference:
+ # https://gitlab.com/procps-ng/procps/blob/v3.3.12/top/top.c#L5063
+ field_delta = max(0, field_delta)
+ field_deltas.append(field_delta)
+ return _psplatform.scputimes(*field_deltas)
+
+
+def cpu_percent(interval=None, percpu=False):
+ """Return a float representing the current system-wide CPU
+ utilization as a percentage.
+
+ When *interval* is > 0.0 compares system CPU times elapsed before
+ and after the interval (blocking).
+
+ When *interval* is 0.0 or None compares system CPU times elapsed
+ since last call or module import, returning immediately (non
+ blocking). That means the first time this is called it will
+ return a meaningless 0.0 value which you should ignore.
+ In this case is recommended for accuracy that this function be
+ called with at least 0.1 seconds between calls.
+
+ When *percpu* is True returns a list of floats representing the
+ utilization as a percentage for each CPU.
+ First element of the list refers to first CPU, second element
+ to second CPU and so on.
+ The order of the list is consistent across calls.
+
+ Examples:
+
+ >>> # blocking, system-wide
+ >>> psutil.cpu_percent(interval=1)
+ 2.0
+ >>>
+ >>> # blocking, per-cpu
+ >>> psutil.cpu_percent(interval=1, percpu=True)
+ [2.0, 1.0]
+ >>>
+ >>> # non-blocking (percentage since last call)
+ >>> psutil.cpu_percent(interval=None)
+ 2.9
+ >>>
+ """
+ global _last_cpu_times
+ global _last_per_cpu_times
+ 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)
+
+ 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)
+
+ try:
+ busy_perc = (busy_delta / all_delta) * 100
+ except ZeroDivisionError:
+ return 0.0
+ else:
+ return round(busy_perc, 1)
+
+ # system-wide usage
+ if not percpu:
+ if blocking:
+ 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)
+ # per-cpu usage
+ else:
+ ret = []
+ if blocking:
+ 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):
+ 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
+
+
+def cpu_times_percent(interval=None, percpu=False):
+ """Same as cpu_percent() but provides utilization percentages
+ for each specific CPU time as is returned by cpu_times().
+ For instance, on Linux we'll get:
+
+ >>> cpu_times_percent()
+ cpupercent(user=4.8, nice=0.0, system=4.8, idle=90.5, iowait=0.0,
+ irq=0.0, softirq=0.0, steal=0.0, guest=0.0, guest_nice=0.0)
+ >>>
+
+ *interval* and *percpu* arguments have the same meaning as in
+ cpu_percent().
+ """
+ global _last_cpu_times_2
+ global _last_per_cpu_times_2
+ 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)
+
+ def calculate(t1, t2):
+ nums = []
+ times_delta = _cpu_times_deltas(t1, t2)
+ all_delta = _cpu_tot_time(times_delta)
+ # "scale" is the value to multiply each delta with to get percentages.
+ # We use "max" to avoid division by zero (if all_delta is 0, then all
+ # fields are 0 so percentages will be 0 too. all_delta cannot be a
+ # fraction because cpu times are integers)
+ scale = 100.0 / max(1, all_delta)
+ for field_delta in times_delta:
+ field_perc = field_delta * scale
+ field_perc = round(field_perc, 1)
+ # make sure we don't return negative values or values over 100%
+ field_perc = min(max(0.0, field_perc), 100.0)
+ nums.append(field_perc)
+ return _psplatform.scputimes(*nums)
+
+ # system-wide usage
+ if not percpu:
+ if blocking:
+ 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)
+ # per-cpu usage
+ else:
+ ret = []
+ if blocking:
+ 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):
+ ret.append(calculate(t1, t2))
+ return ret
+
+
+def cpu_stats():
+ """Return CPU statistics."""
+ return _psplatform.cpu_stats()
+
+
+if hasattr(_psplatform, "cpu_freq"):
+
+ def cpu_freq(percpu=False):
+ """Return CPU frequency as a nameduple including current,
+ min and max frequency expressed in Mhz.
+
+ If *percpu* is True and the system supports per-cpu frequency
+ retrieval (Linux only) a list of frequencies is returned for
+ each CPU. If not a list with one element is returned.
+ """
+ ret = _psplatform.cpu_freq()
+ if percpu:
+ return ret
+ else:
+ num_cpus = float(len(ret))
+ if num_cpus == 0:
+ return None
+ elif num_cpus == 1:
+ return ret[0]
+ else:
+ currs, mins, maxs = 0.0, 0.0, 0.0
+ set_none = False
+ for cpu in ret:
+ currs += cpu.current
+ # On Linux if /proc/cpuinfo is used min/max are set
+ # to None.
+ if LINUX and cpu.min is None:
+ set_none = True
+ continue
+ mins += cpu.min
+ maxs += cpu.max
+
+ current = currs / num_cpus
+
+ if set_none:
+ min_ = max_ = None
+ else:
+ min_ = mins / num_cpus
+ max_ = maxs / num_cpus
+
+ return _common.scpufreq(current, min_, max_)
+
+ __all__.append("cpu_freq")
+
+
+if hasattr(os, "getloadavg") or hasattr(_psplatform, "getloadavg"):
+ # Perform this hasattr check once on import time to either use the
+ # platform based code or proxy straight from the os module.
+ if hasattr(os, "getloadavg"):
+ getloadavg = os.getloadavg
+ else:
+ getloadavg = _psplatform.getloadavg
+
+ __all__.append("getloadavg")
+
+
+# =====================================================================
+# --- system memory related functions
+# =====================================================================
+
+
+def virtual_memory():
+ """Return statistics about system memory usage as a namedtuple
+ including the following fields, expressed in bytes:
+
+ - total:
+ total physical memory available.
+
+ - available:
+ the memory that can be given instantly to processes without the
+ system going into swap.
+ This is calculated by summing different memory values depending
+ on the platform and it is supposed to be used to monitor actual
+ memory usage in a cross platform fashion.
+
+ - percent:
+ the percentage usage calculated as (total - available) / total * 100
+
+ - used:
+ memory used, calculated differently depending on the platform and
+ designed for informational purposes only:
+ macOS: active + wired
+ BSD: active + wired + cached
+ Linux: total - free
+
+ - free:
+ memory not being used at all (zeroed) that is readily available;
+ note that this doesn't reflect the actual memory available
+ (use 'available' instead)
+
+ Platform-specific fields:
+
+ - active (UNIX):
+ memory currently in use or very recently used, and so it is in RAM.
+
+ - inactive (UNIX):
+ memory that is marked as not used.
+
+ - buffers (BSD, Linux):
+ cache for things like file system metadata.
+
+ - cached (BSD, macOS):
+ cache for various things.
+
+ - wired (macOS, BSD):
+ memory that is marked to always stay in RAM. It is never moved to disk.
+
+ - shared (BSD):
+ memory that may be simultaneously accessed by multiple processes.
+
+ The sum of 'used' and 'available' does not necessarily equal total.
+ On Windows 'available' and 'free' are the same.
+ """
+ global _TOTAL_PHYMEM
+ ret = _psplatform.virtual_memory()
+ # cached for later use in Process.memory_percent()
+ _TOTAL_PHYMEM = ret.total
+ return ret
+
+
+def swap_memory():
+ """Return system swap memory statistics as a namedtuple including
+ the following fields:
+
+ - total: total swap memory in bytes
+ - used: used swap memory in bytes
+ - free: free swap memory in bytes
+ - percent: the percentage usage
+ - sin: no. of bytes the system has swapped in from disk (cumulative)
+ - sout: no. of bytes the system has swapped out from disk (cumulative)
+
+ 'sin' and 'sout' on Windows are meaningless and always set to 0.
+ """
+ return _psplatform.swap_memory()
+
+
+# =====================================================================
+# --- disks/paritions related functions
+# =====================================================================
+
+
+def disk_usage(path):
+ """Return disk usage statistics about the given *path* as a
+ namedtuple including total, used and free space expressed in bytes
+ plus the percentage usage.
+ """
+ return _psplatform.disk_usage(path)
+
+
+def disk_partitions(all=False):
+ """Return mounted partitions as a list of
+ (device, mountpoint, fstype, opts) namedtuple.
+ 'opts' field is a raw string separated by commas indicating mount
+ options which may vary depending on the platform.
+
+ If *all* parameter is False return physical devices only and ignore
+ all others.
+ """
+ def pathconf(path, name):
+ try:
+ return os.pathconf(path, name)
+ except (OSError, AttributeError):
+ pass
+
+ ret = _psplatform.disk_partitions(all)
+ if POSIX:
+ new = []
+ for item in ret:
+ nt = item._replace(
+ maxfile=pathconf(item.mountpoint, 'PC_NAME_MAX'),
+ maxpath=pathconf(item.mountpoint, 'PC_PATH_MAX'))
+ new.append(nt)
+ return new
+ else:
+ return ret
+
+
+def disk_io_counters(perdisk=False, nowrap=True):
+ """Return system disk I/O statistics as a namedtuple including
+ the following fields:
+
+ - read_count: number of reads
+ - write_count: number of writes
+ - read_bytes: number of bytes read
+ - write_bytes: number of bytes written
+ - read_time: time spent reading from disk (in ms)
+ - write_time: time spent writing to disk (in ms)
+
+ Platform specific:
+
+ - busy_time: (Linux, FreeBSD) time spent doing actual I/Os (in ms)
+ - read_merged_count (Linux): number of merged reads
+ - write_merged_count (Linux): number of merged writes
+
+ If *perdisk* is True return the same information for every
+ physical disk installed on the system as a dictionary
+ with partition names as the keys and the namedtuple
+ described above as the values.
+
+ If *nowrap* is True it detects and adjust the numbers which overflow
+ 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
+ cache.
+
+ On recent Windows versions 'diskperf -y' command may need to be
+ executed first otherwise this function won't find any disk.
+ """
+ kwargs = dict(perdisk=perdisk) if LINUX else {}
+ rawdict = _psplatform.disk_io_counters(**kwargs)
+ if not rawdict:
+ return {} if perdisk else None
+ if nowrap:
+ rawdict = _wrap_numbers(rawdict, 'psutil.disk_io_counters')
+ nt = getattr(_psplatform, "sdiskio", _common.sdiskio)
+ if perdisk:
+ for disk, fields in rawdict.items():
+ rawdict[disk] = nt(*fields)
+ return rawdict
+ else:
+ 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')
+disk_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache"
+
+
+# =====================================================================
+# --- network related functions
+# =====================================================================
+
+
+def net_io_counters(pernic=False, nowrap=True):
+ """Return network I/O statistics as a namedtuple including
+ the following fields:
+
+ - bytes_sent: number of bytes sent
+ - bytes_recv: number of bytes received
+ - packets_sent: number of packets sent
+ - packets_recv: number of packets received
+ - errin: total number of errors while receiving
+ - errout: total number of errors while sending
+ - dropin: total number of incoming packets which were dropped
+ - dropout: total number of outgoing packets which were dropped
+ (always 0 on macOS and BSD)
+
+ If *pernic* is True return the same information for every
+ network interface installed on the system as a dictionary
+ with network interface names as the keys and the namedtuple
+ described above as the values.
+
+ If *nowrap* is True it detects and adjust the numbers which overflow
+ 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
+ cache.
+ """
+ rawdict = _psplatform.net_io_counters()
+ if not rawdict:
+ return {} if pernic else None
+ if nowrap:
+ rawdict = _wrap_numbers(rawdict, 'psutil.net_io_counters')
+ if pernic:
+ for nic, fields in rawdict.items():
+ rawdict[nic] = _common.snetio(*fields)
+ return rawdict
+ else:
+ return _common.snetio(*[sum(x) for x in zip(*rawdict.values())])
+
+
+net_io_counters.cache_clear = functools.partial(
+ _wrap_numbers.cache_clear, 'psutil.net_io_counters')
+net_io_counters.cache_clear.__doc__ = "Clears nowrap argument cache"
+
+
+def net_connections(kind='inet'):
+ """Return system-wide socket connections as a list of
+ (fd, family, type, laddr, raddr, status, pid) namedtuples.
+ In case of limited privileges 'fd' and 'pid' may be set to -1
+ and None respectively.
+ The *kind* parameter filters for connections that fit the
+ following criteria:
+
+ +------------+----------------------------------------------------+
+ | Kind Value | Connections using |
+ +------------+----------------------------------------------------+
+ | inet | IPv4 and IPv6 |
+ | inet4 | IPv4 |
+ | inet6 | IPv6 |
+ | tcp | TCP |
+ | tcp4 | TCP over IPv4 |
+ | tcp6 | TCP over IPv6 |
+ | udp | UDP |
+ | udp4 | UDP over IPv4 |
+ | udp6 | UDP over IPv6 |
+ | unix | UNIX socket (both UDP and TCP protocols) |
+ | all | the sum of all the possible families and protocols |
+ +------------+----------------------------------------------------+
+
+ On macOS this function requires root privileges.
+ """
+ return _psplatform.net_connections(kind)
+
+
+def net_if_addrs():
+ """Return the addresses associated to each NIC (network interface
+ card) installed on the system as a dictionary whose keys are the
+ NIC names and value is a list of namedtuples for each address
+ assigned to the NIC. Each namedtuple includes 5 fields:
+
+ - family: can be either socket.AF_INET, socket.AF_INET6 or
+ psutil.AF_LINK, which refers to a MAC address.
+ - address: is the primary address and it is always set.
+ - netmask: and 'broadcast' and 'ptp' may be None.
+ - ptp: stands for "point to point" and references the
+ destination address on a point to point interface
+ (typically a VPN).
+ - broadcast: and *ptp* are mutually exclusive.
+
+ Note: you can have more than one address of the same family
+ associated with each interface.
+ """
+ has_enums = sys.version_info >= (3, 4)
+ if has_enums:
+ import socket
+ rawlist = _psplatform.net_if_addrs()
+ rawlist.sort(key=lambda x: x[1]) # sort by family
+ ret = collections.defaultdict(list)
+ for name, fam, addr, mask, broadcast, ptp in rawlist:
+ if has_enums:
+ try:
+ fam = socket.AddressFamily(fam)
+ except ValueError:
+ if WINDOWS and fam == -1:
+ fam = _psplatform.AF_LINK
+ elif (hasattr(_psplatform, "AF_LINK") and
+ _psplatform.AF_LINK == fam):
+ # 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
+ fam = _psplatform.AF_LINK
+ if fam == _psplatform.AF_LINK:
+ # The underlying C function may return an incomplete MAC
+ # address in which case we fill it with null bytes, see:
+ # https://github.com/giampaolo/psutil/issues/786
+ separator = ":" if POSIX else "-"
+ while addr.count(separator) < 5:
+ addr += "%s00" % separator
+ ret[name].append(_common.snicaddr(fam, addr, mask, broadcast, ptp))
+ return dict(ret)
+
+
+def net_if_stats():
+ """Return information about each NIC (network interface card)
+ installed on the system as a dictionary whose keys are the
+ NIC names and value is a namedtuple with the following fields:
+
+ - isup: whether the interface is up (bool)
+ - duplex: can be either NIC_DUPLEX_FULL, NIC_DUPLEX_HALF or
+ NIC_DUPLEX_UNKNOWN
+ - speed: the NIC speed expressed in mega bits (MB); if it can't
+ be determined (e.g. 'localhost') it will be set to 0.
+ - mtu: the maximum transmission unit expressed in bytes.
+ """
+ return _psplatform.net_if_stats()
+
+
+# =====================================================================
+# --- sensors
+# =====================================================================
+
+
+# Linux, macOS
+if hasattr(_psplatform, "sensors_temperatures"):
+
+ def sensors_temperatures(fahrenheit=False):
+ """Return hardware temperatures. Each entry is a namedtuple
+ representing a certain hardware sensor (it may be a CPU, an
+ hard disk or something else, depending on the OS and its
+ configuration).
+ 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
+
+ ret = collections.defaultdict(list)
+ rawdict = _psplatform.sensors_temperatures()
+
+ for name, values in rawdict.items():
+ while values:
+ label, current, high, critical = values.pop(0)
+ current = convert(current)
+ high = convert(high)
+ critical = convert(critical)
+
+ if high and not critical:
+ critical = high
+ elif critical and not high:
+ high = critical
+
+ ret[name].append(
+ _common.shwtemp(label, current, high, critical))
+
+ return dict(ret)
+
+ __all__.append("sensors_temperatures")
+
+
+# Linux
+if hasattr(_psplatform, "sensors_fans"):
+
+ def sensors_fans():
+ """Return fans speed. Each entry is a namedtuple
+ representing a certain hardware sensor.
+ All speed are expressed in RPM (rounds per minute).
+ """
+ return _psplatform.sensors_fans()
+
+ __all__.append("sensors_fans")
+
+
+# Linux, Windows, FreeBSD, macOS
+if hasattr(_psplatform, "sensors_battery"):
+
+ def sensors_battery():
+ """Return battery information. If no battery is installed
+ returns None.
+
+ - percent: battery power left as a percentage.
+ - secsleft: a rough approximation of how many seconds are left
+ before the battery runs out of power. May be
+ POWER_TIME_UNLIMITED or POWER_TIME_UNLIMITED.
+ - power_plugged: True if the AC power cable is connected.
+ """
+ return _psplatform.sensors_battery()
+
+ __all__.append("sensors_battery")
+
+
+# =====================================================================
+# --- other system related functions
+# =====================================================================
+
+
+def boot_time():
+ """Return the system boot time expressed in seconds since the epoch."""
+ # Note: we are not caching this because it is subject to
+ # system clock updates.
+ return _psplatform.boot_time()
+
+
+def users():
+ """Return users currently connected on the system as a list of
+ namedtuples including the following fields.
+
+ - user: the name of the user
+ - terminal: the tty or pseudo-tty associated with the user, if any.
+ - host: the host name associated with the entry, if any.
+ - started: the creation time as a floating point number expressed in
+ seconds since the epoch.
+ """
+ return _psplatform.users()
+
+
+# =====================================================================
+# --- Windows services
+# =====================================================================
+
+
+if WINDOWS:
+
+ def win_service_iter():
+ """Return a generator yielding a WindowsService instance for all
+ Windows services installed.
+ """
+ return _psplatform.win_service_iter()
+
+ def win_service_get(name):
+ """Get a Windows service by *name*.
+ Raise NoSuchProcess if no service with such name exists.
+ """
+ return _psplatform.win_service_get(name)
+
+
+# =====================================================================
+
+
+def test(): # pragma: no cover
+ from ._common import bytes2human
+ from ._compat import get_terminal_size
+
+ today_day = datetime.date.today()
+ 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"))
+ for p in process_iter(attrs, ad_value=None):
+ if p.info['create_time']:
+ ctime = datetime.datetime.fromtimestamp(p.info['create_time'])
+ if ctime.date() == today_day:
+ ctime = ctime.strftime("%H:%M")
+ else:
+ ctime = ctime.strftime("%b%d")
+ else:
+ ctime = ''
+ if p.info['cpu_times']:
+ cputime = time.strftime("%M:%S",
+ time.localtime(sum(p.info['cpu_times'])))
+ else:
+ cputime = ''
+
+ user = p.info['username'] or ''
+ if not user and POSIX:
+ try:
+ user = p.uids()[0]
+ except Error:
+ pass
+ 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 ''
+ nice = int(p.info['nice']) if p.info['nice'] else ''
+ if p.info['cmdline']:
+ cmdline = ' '.join(p.info['cmdline'])
+ else:
+ cmdline = p.info['name']
+ status = p.info['status'][:5] if p.info['status'] else ''
+
+ line = templ % (
+ user[:10],
+ p.info['pid'],
+ memp,
+ vms,
+ rss,
+ nice,
+ status,
+ ctime,
+ cputime,
+ cmdline)
+ print(line[:get_terminal_size()[0]]) # NOQA
+
+
+del memoize_when_activated, division
+if sys.version_info[0] < 3:
+ del num, x
+
+if __name__ == "__main__":
+ test()
diff --git a/contrib/python/psutil/py3/psutil/_common.py b/contrib/python/psutil/py3/psutil/_common.py
new file mode 100644
index 0000000000..771461d692
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_common.py
@@ -0,0 +1,846 @@
+# 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.
+
+"""Common objects shared by __init__.py and _ps*.py modules."""
+
+# Note: this module is imported by setup.py so it should not import
+# psutil or third-party modules.
+
+from __future__ import division, print_function
+
+import contextlib
+import errno
+import functools
+import os
+import socket
+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:
+ AF_INET6 = None
+try:
+ from socket import AF_UNIX
+except ImportError:
+ AF_UNIX = None
+
+if sys.version_info >= (3, 4):
+ 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
+
+__all__ = [
+ # OS constants
+ 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX',
+ 'SUNOS', 'WINDOWS',
+ # connection constants
+ 'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED',
+ 'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN',
+ 'CONN_NONE', 'CONN_SYN_RECV', 'CONN_SYN_SENT', 'CONN_TIME_WAIT',
+ # net constants
+ 'NIC_DUPLEX_FULL', 'NIC_DUPLEX_HALF', 'NIC_DUPLEX_UNKNOWN',
+ # process status constants
+ 'STATUS_DEAD', 'STATUS_DISK_SLEEP', 'STATUS_IDLE', 'STATUS_LOCKED',
+ 'STATUS_RUNNING', 'STATUS_SLEEPING', 'STATUS_STOPPED', 'STATUS_SUSPENDED',
+ 'STATUS_TRACING_STOP', 'STATUS_WAITING', 'STATUS_WAKE_KILL',
+ 'STATUS_WAKING', 'STATUS_ZOMBIE', 'STATUS_PARKED',
+ # other constants
+ 'ENCODING', 'ENCODING_ERRS', 'AF_INET6',
+ # named tuples
+ 'pconn', 'pcputimes', 'pctxsw', 'pgids', 'pio', 'pionice', 'popenfile',
+ 'pthread', 'puids', 'sconn', 'scpustats', 'sdiskio', 'sdiskpart',
+ 'sdiskusage', 'snetio', 'snicaddr', 'snicstats', 'sswap', 'suser',
+ # utility functions
+ '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",
+ 'bytes2human', 'conn_to_ntuple', 'debug',
+ # shell utils
+ 'hilite', 'term_supports_colors', 'print_color',
+]
+
+
+# ===================================================================
+# --- OS constants
+# ===================================================================
+
+
+POSIX = os.name == "posix"
+WINDOWS = os.name == "nt"
+LINUX = sys.platform.startswith("linux")
+MACOS = sys.platform.startswith("darwin")
+OSX = MACOS # deprecated alias
+FREEBSD = sys.platform.startswith("freebsd")
+OPENBSD = sys.platform.startswith("openbsd")
+NETBSD = sys.platform.startswith("netbsd")
+BSD = FREEBSD or OPENBSD or NETBSD
+SUNOS = sys.platform.startswith(("sunos", "solaris"))
+AIX = sys.platform.startswith("aix")
+
+
+# ===================================================================
+# --- API constants
+# ===================================================================
+
+
+# Process.status()
+STATUS_RUNNING = "running"
+STATUS_SLEEPING = "sleeping"
+STATUS_DISK_SLEEP = "disk-sleep"
+STATUS_STOPPED = "stopped"
+STATUS_TRACING_STOP = "tracing-stop"
+STATUS_ZOMBIE = "zombie"
+STATUS_DEAD = "dead"
+STATUS_WAKE_KILL = "wake-kill"
+STATUS_WAKING = "waking"
+STATUS_IDLE = "idle" # Linux, macOS, FreeBSD
+STATUS_LOCKED = "locked" # FreeBSD
+STATUS_WAITING = "waiting" # FreeBSD
+STATUS_SUSPENDED = "suspended" # NetBSD
+STATUS_PARKED = "parked" # Linux
+
+# Process.connections() and psutil.net_connections()
+CONN_ESTABLISHED = "ESTABLISHED"
+CONN_SYN_SENT = "SYN_SENT"
+CONN_SYN_RECV = "SYN_RECV"
+CONN_FIN_WAIT1 = "FIN_WAIT1"
+CONN_FIN_WAIT2 = "FIN_WAIT2"
+CONN_TIME_WAIT = "TIME_WAIT"
+CONN_CLOSE = "CLOSE"
+CONN_CLOSE_WAIT = "CLOSE_WAIT"
+CONN_LAST_ACK = "LAST_ACK"
+CONN_LISTEN = "LISTEN"
+CONN_CLOSING = "CLOSING"
+CONN_NONE = "NONE"
+
+# net_if_stats()
+if enum is None:
+ NIC_DUPLEX_FULL = 2
+ NIC_DUPLEX_HALF = 1
+ NIC_DUPLEX_UNKNOWN = 0
+else:
+ class NicDuplex(enum.IntEnum):
+ NIC_DUPLEX_FULL = 2
+ NIC_DUPLEX_HALF = 1
+ NIC_DUPLEX_UNKNOWN = 0
+
+ globals().update(NicDuplex.__members__)
+
+# sensors_battery()
+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
+
+ globals().update(BatteryTime.__members__)
+
+# --- others
+
+ENCODING = sys.getfilesystemencoding()
+if not PY3:
+ ENCODING_ERRS = "replace"
+else:
+ try:
+ ENCODING_ERRS = sys.getfilesystemencodeerrors() # py 3.6
+ except AttributeError:
+ ENCODING_ERRS = "surrogateescape" if POSIX else "replace"
+
+
+# ===================================================================
+# --- namedtuples
+# ===================================================================
+
+# --- for system functions
+
+# psutil.swap_memory()
+sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin',
+ 'sout'])
+# psutil.disk_usage()
+sdiskusage = namedtuple('sdiskusage', ['total', 'used', 'free', 'percent'])
+# psutil.disk_io_counters()
+sdiskio = namedtuple('sdiskio', ['read_count', 'write_count',
+ 'read_bytes', 'write_bytes',
+ 'read_time', 'write_time'])
+# psutil.disk_partitions()
+sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts',
+ 'maxfile', 'maxpath'])
+# psutil.net_io_counters()
+snetio = namedtuple('snetio', ['bytes_sent', 'bytes_recv',
+ 'packets_sent', 'packets_recv',
+ 'errin', 'errout',
+ 'dropin', 'dropout'])
+# psutil.users()
+suser = namedtuple('suser', ['name', 'terminal', 'host', 'started', 'pid'])
+# psutil.net_connections()
+sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
+ 'status', 'pid'])
+# psutil.net_if_addrs()
+snicaddr = namedtuple('snicaddr',
+ ['family', 'address', 'netmask', 'broadcast', 'ptp'])
+# psutil.net_if_stats()
+snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu'])
+# psutil.cpu_stats()
+scpustats = namedtuple(
+ 'scpustats', ['ctx_switches', 'interrupts', 'soft_interrupts', 'syscalls'])
+# psutil.cpu_freq()
+scpufreq = namedtuple('scpufreq', ['current', 'min', 'max'])
+# psutil.sensors_temperatures()
+shwtemp = namedtuple(
+ 'shwtemp', ['label', 'current', 'high', 'critical'])
+# psutil.sensors_battery()
+sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged'])
+# psutil.sensors_fans()
+sfan = namedtuple('sfan', ['label', 'current'])
+
+# --- for Process methods
+
+# psutil.Process.cpu_times()
+pcputimes = namedtuple('pcputimes',
+ ['user', 'system', 'children_user', 'children_system'])
+# psutil.Process.open_files()
+popenfile = namedtuple('popenfile', ['path', 'fd'])
+# psutil.Process.threads()
+pthread = namedtuple('pthread', ['id', 'user_time', 'system_time'])
+# psutil.Process.uids()
+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'])
+# 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'])
+
+# psutil.connections() and psutil.Process.connections()
+addr = namedtuple('addr', ['ip', 'port'])
+
+
+# ===================================================================
+# --- Process.connections() 'kind' parameter mapping
+# ===================================================================
+
+
+conn_tmap = {
+ "all": ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
+ "tcp": ([AF_INET, AF_INET6], [SOCK_STREAM]),
+ "tcp4": ([AF_INET], [SOCK_STREAM]),
+ "udp": ([AF_INET, AF_INET6], [SOCK_DGRAM]),
+ "udp4": ([AF_INET], [SOCK_DGRAM]),
+ "inet": ([AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),
+ "inet4": ([AF_INET], [SOCK_STREAM, SOCK_DGRAM]),
+ "inet6": ([AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),
+}
+
+if AF_INET6 is not None:
+ conn_tmap.update({
+ "tcp6": ([AF_INET6], [SOCK_STREAM]),
+ "udp6": ([AF_INET6], [SOCK_DGRAM]),
+ })
+
+if AF_UNIX is not None:
+ conn_tmap.update({
+ "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
+ })
+
+
+# =====================================================================
+# --- Exceptions
+# =====================================================================
+
+
+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 __repr__(self):
+ ret = "psutil.%s %s" % (self.__class__.__name__, self.msg)
+ return ret.strip()
+
+ __str__ = __repr__
+
+
+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)
+ 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
+
+
+class ZombieProcess(NoSuchProcess):
+ """Exception raised when querying a zombie process. This is
+ raised on macOS, BSD and Solaris only, and not always: depending
+ on the query the OS may be able to succeed anyway.
+ 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
+ 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
+
+
+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)
+ 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 = ""
+
+
+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)
+ 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
+
+
+# ===================================================================
+# --- utils
+# ===================================================================
+
+
+def usage_percent(used, total, round_=None):
+ """Calculate percentage usage of 'used' against 'total'."""
+ try:
+ ret = (float(used) / total) * 100
+ except ZeroDivisionError:
+ return 0.0
+ else:
+ if round_ is not None:
+ ret = round(ret, round_)
+ return ret
+
+
+def memoize(fun):
+ """A simple memoize decorator for functions supporting (hashable)
+ positional arguments.
+ It also provides a cache_clear() function for clearing the cache:
+
+ >>> @memoize
+ ... def foo()
+ ... return 1
+ ...
+ >>> foo()
+ 1
+ >>> foo.cache_clear()
+ >>>
+ """
+ @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)
+ return ret
+
+ def cache_clear():
+ """Clear cache."""
+ cache.clear()
+
+ cache = {}
+ wrapper.cache_clear = cache_clear
+ return wrapper
+
+
+def memoize_when_activated(fun):
+ """A memoize decorator which is disabled by default. It can be
+ activated and deactivated on request.
+ For efficiency reasons it can be used only against class methods
+ accepting no arguments.
+
+ >>> class Foo:
+ ... @memoize
+ ... def foo()
+ ... print(1)
+ ...
+ >>> f = Foo()
+ >>> # deactivated (default)
+ >>> foo()
+ 1
+ >>> foo()
+ 1
+ >>>
+ >>> # activated
+ >>> foo.cache_activate(self)
+ >>> foo()
+ 1
+ >>> foo()
+ >>> foo()
+ >>>
+ """
+ @functools.wraps(fun)
+ def wrapper(self):
+ try:
+ # case 1: we previously entered oneshot() ctx
+ ret = self._cache[fun]
+ except AttributeError:
+ # case 2: we never entered oneshot() ctx
+ return fun(self)
+ except KeyError:
+ # case 3: we entered oneshot() ctx but there's no cache
+ # for this entry yet
+ ret = self._cache[fun] = fun(self)
+ return ret
+
+ def cache_activate(proc):
+ """Activate cache. Expects a Process instance. Cache will be
+ stored as a "_cache" instance attribute."""
+ proc._cache = {}
+
+ def cache_deactivate(proc):
+ """Deactivate and clear cache."""
+ try:
+ del proc._cache
+ except AttributeError:
+ pass
+
+ wrapper.cache_activate = cache_activate
+ wrapper.cache_deactivate = cache_deactivate
+ return wrapper
+
+
+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
+ """
+ try:
+ st = os.stat(path)
+ except OSError as err:
+ if err.errno in (errno.EPERM, errno.EACCES):
+ raise
+ return False
+ else:
+ return stat.S_ISREG(st.st_mode)
+
+
+def path_exists_strict(path):
+ """Same as os.path.exists() but does not swallow EACCES / EPERM
+ exceptions, see:
+ http://mail.python.org/pipermail/python-dev/2012-June/120787.html
+ """
+ try:
+ os.stat(path)
+ except OSError as err:
+ if err.errno in (errno.EPERM, errno.EACCES):
+ raise
+ return False
+ else:
+ return True
+
+
+@memoize
+def supports_ipv6():
+ """Return True if IPv6 is supported on this platform."""
+ if not socket.has_ipv6 or AF_INET6 is None:
+ return False
+ try:
+ sock = socket.socket(AF_INET6, socket.SOCK_STREAM)
+ with contextlib.closing(sock):
+ sock.bind(("::1", 0))
+ return True
+ except socket.error:
+ return False
+
+
+def parse_environ_block(data):
+ """Parse a C environ block of environment variables into a dictionary."""
+ # The block is usually raw data from the target process. It might contain
+ # trailing garbage and lines that do not look like assignments.
+ ret = {}
+ pos = 0
+
+ # localize global variable to speed up access.
+ WINDOWS_ = WINDOWS
+ while True:
+ next_pos = data.find("\0", pos)
+ # nul byte at the beginning or double nul byte means finish
+ if next_pos <= pos:
+ break
+ # there might not be an equals sign
+ equal_pos = data.find("=", pos, next_pos)
+ if equal_pos > pos:
+ key = data[pos:equal_pos]
+ value = data[equal_pos + 1:next_pos]
+ # Windows expects environment variables to be uppercase only
+ if WINDOWS_:
+ key = key.upper()
+ ret[key] = value
+ pos = next_pos + 1
+
+ return ret
+
+
+def sockfam_to_enum(num):
+ """Convert a numeric socket family value to an IntEnum member.
+ If it's not a known member, return the numeric value itself.
+ """
+ if enum is None:
+ return num
+ else: # pragma: no cover
+ try:
+ return socket.AddressFamily(num)
+ except ValueError:
+ return num
+
+
+def socktype_to_enum(num):
+ """Convert a numeric socket type value to an IntEnum member.
+ If it's not a known member, return the numeric value itself.
+ """
+ if enum is None:
+ return num
+ else: # pragma: no cover
+ try:
+ return socket.SocketKind(num)
+ except ValueError:
+ return num
+
+
+def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None):
+ """Convert a raw connection tuple to a proper ntuple."""
+ if fam in (socket.AF_INET, AF_INET6):
+ if laddr:
+ laddr = addr(*laddr)
+ if raddr:
+ raddr = addr(*raddr)
+ if type_ == socket.SOCK_STREAM and fam in (AF_INET, AF_INET6):
+ status = status_map.get(status, CONN_NONE)
+ else:
+ status = CONN_NONE # ignore whatever C returned to us
+ fam = sockfam_to_enum(fam)
+ type_ = socktype_to_enum(type_)
+ if pid is None:
+ return pconn(fd, fam, type_, laddr, raddr, status)
+ else:
+ return sconn(fd, fam, type_, laddr, raddr, status, pid)
+
+
+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)
+ if fun.__doc__ is None:
+ fun.__doc__ = msg
+
+ @functools.wraps(fun)
+ def inner(self, *args, **kwargs):
+ warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
+ return getattr(self, replacement)(*args, **kwargs)
+ return inner
+ return outer
+
+
+class _WrapNumbers:
+ """Watches numbers so that they don't overflow and wrap
+ (reset to zero).
+ """
+
+ def __init__(self):
+ self.lock = threading.Lock()
+ self.cache = {}
+ self.reminders = {}
+ self.reminder_keys = {}
+
+ def _add_dict(self, input_dict, name):
+ assert name not in self.cache
+ 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)
+
+ def _remove_dead_reminders(self, input_dict, name):
+ """In case the number of keys changed between calls (e.g. a
+ disk disappears) this removes the entry from self.reminders.
+ """
+ old_dict = self.cache[name]
+ gone_keys = set(old_dict.keys()) - set(input_dict.keys())
+ for gone_key in gone_keys:
+ for remkey in self.reminder_keys[name][gone_key]:
+ del self.reminders[name][remkey]
+ del self.reminder_keys[name][gone_key]
+
+ def run(self, input_dict, name):
+ """Cache dict and sum numbers which overflow and wrap.
+ Return an updated copy of `input_dict`
+ """
+ if name not in self.cache:
+ # This was the first call.
+ self._add_dict(input_dict, name)
+ return input_dict
+
+ self._remove_dead_reminders(input_dict, name)
+
+ old_dict = self.cache[name]
+ new_dict = {}
+ for key in input_dict.keys():
+ input_tuple = input_dict[key]
+ try:
+ old_tuple = old_dict[key]
+ except KeyError:
+ # The input dict has a new key (e.g. a new disk or NIC)
+ # which didn't exist in the previous call.
+ new_dict[key] = input_tuple
+ continue
+
+ bits = []
+ for i in range(len(input_tuple)):
+ input_value = input_tuple[i]
+ old_value = old_tuple[i]
+ remkey = (key, i)
+ if input_value < old_value:
+ # it wrapped!
+ self.reminders[name][remkey] += old_value
+ self.reminder_keys[name][key].add(remkey)
+ bits.append(input_value + self.reminders[name][remkey])
+
+ new_dict[key] = tuple(bits)
+
+ self.cache[name] = input_dict
+ return new_dict
+
+ def cache_clear(self, name=None):
+ """Clear the internal cache, optionally only for function 'name'."""
+ with self.lock:
+ if name is None:
+ self.cache.clear()
+ self.reminders.clear()
+ self.reminder_keys.clear()
+ else:
+ self.cache.pop(name, None)
+ self.reminders.pop(name, None)
+ self.reminder_keys.pop(name, None)
+
+ def cache_info(self):
+ """Return internal cache dicts as a tuple of 3 elements."""
+ with self.lock:
+ return (self.cache, self.reminders, self.reminder_keys)
+
+
+def wrap_numbers(input_dict, name):
+ """Given an `input_dict` and a function `name`, adjust the numbers
+ which "wrap" (restart from zero) across different calls by adding
+ "old value" to "new value" and return an updated dict.
+ """
+ with _wn.lock:
+ return _wn.run(input_dict, name)
+
+
+_wn = _WrapNumbers()
+wrap_numbers.cache_clear = _wn.cache_clear
+wrap_numbers.cache_info = _wn.cache_info
+
+
+def open_binary(fname, **kwargs):
+ return open(fname, "rb", **kwargs)
+
+
+def open_text(fname, **kwargs):
+ """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)
+
+
+def bytes2human(n, format="%(value).1f%(symbol)s"):
+ """Used by various scripts. See:
+ http://goo.gl/zeJZl
+
+ >>> bytes2human(10000)
+ '9.8K'
+ >>> bytes2human(100001221)
+ '95.4M'
+ """
+ symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
+ prefix = {}
+ for i, s in enumerate(symbols[1:]):
+ prefix[s] = 1 << (i + 1) * 10
+ for symbol in reversed(symbols[1:]):
+ if n >= prefix[symbol]:
+ value = float(n) / prefix[symbol]
+ return format % locals()
+ return format % dict(symbol=symbols[0], value=n)
+
+
+def get_procfs_path():
+ """Return updated psutil.PROCFS_PATH constant."""
+ return sys.modules['psutil'].PROCFS_PATH
+
+
+if PY3:
+ def decode(s):
+ return s.decode(encoding=ENCODING, errors=ENCODING_ERRS)
+else:
+ def decode(s):
+ return s
+
+
+# =====================================================================
+# --- shell utils
+# =====================================================================
+
+
+@memoize
+def term_supports_colors(file=sys.stdout): # pragma: no cover
+ if os.name == 'nt':
+ return True
+ try:
+ import curses
+ assert file.isatty()
+ curses.setupterm()
+ assert curses.tigetnum("colors") > 0
+ except Exception:
+ return False
+ else:
+ return True
+
+
+def hilite(s, color=None, bold=False): # pragma: no cover
+ """Return an highlighted version of 'string'."""
+ 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[None] = '29'
+ try:
+ color = colors[color]
+ except KeyError:
+ raise ValueError("invalid color %r; choose between %s" % (
+ list(colors.keys())))
+ attr.append(color)
+ if bold:
+ attr.append('1')
+ return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s)
+
+
+def print_color(
+ 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
+ elif POSIX:
+ print(hilite(s, color, bold), file=file) # NOQA
+ else:
+ import ctypes
+
+ DEFAULT_COLOR = 7
+ GetStdHandle = ctypes.windll.Kernel32.GetStdHandle
+ 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())))
+ if bold and color <= 7:
+ color += 8
+
+ handle_id = -12 if file is sys.stderr else -11
+ GetStdHandle.restype = ctypes.c_ulong
+ handle = GetStdHandle(handle_id)
+ SetConsoleTextAttribute(handle, color)
+ try:
+ 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."""
+ 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
diff --git a/contrib/python/psutil/py3/psutil/_compat.py b/contrib/python/psutil/py3/psutil/_compat.py
new file mode 100644
index 0000000000..17f38485e9
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_compat.py
@@ -0,0 +1,424 @@
+# 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.
+
+"""Module which provides compatibility with older Python versions.
+This is more future-compatible rather than the opposite (prefer latest
+Python 3 way of doing things).
+"""
+
+import collections
+import errno
+import functools
+import os
+import sys
+import types
+
+__all__ = [
+ # constants
+ "PY3",
+ # builtins
+ "long", "range", "super", "unicode", "basestring",
+ # literals
+ "u", "b",
+ # collections module
+ "lru_cache",
+ # shutil module
+ "which", "get_terminal_size",
+ # python 3 exceptions
+ "FileNotFoundError", "PermissionError", "ProcessLookupError",
+ "InterruptedError", "ChildProcessError", "FileExistsError"]
+
+
+PY3 = sys.version_info[0] == 3
+_SENTINEL = object()
+
+if PY3:
+ long = int
+ xrange = range
+ unicode = str
+ basestring = str
+ range = range
+
+ def u(s):
+ return s
+
+ def b(s):
+ return s.encode("latin-1")
+else:
+ long = long
+ range = xrange
+ unicode = unicode
+ basestring = basestring
+
+ def u(s):
+ return unicode(s, "unicode_escape")
+
+ def b(s):
+ return s
+
+
+# --- builtins
+
+
+# Python 3 super().
+# Taken from "future" package.
+# Credit: Ryan Kelly
+if PY3:
+ super = super
+else:
+ _builtin_super = super
+
+ def super(type_=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1):
+ """Like Python 3 builtin super(). If called without any arguments
+ it attempts to infer them at runtime.
+ """
+ if type_ is _SENTINEL:
+ f = sys._getframe(framedepth)
+ try:
+ # 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')
+ try:
+ # Get the MRO so we can crawl it.
+ mro = type_or_obj.__mro__
+ except (AttributeError, RuntimeError):
+ try:
+ mro = type_or_obj.__class__.__mro__
+ except AttributeError:
+ raise RuntimeError('super() used in a non-newstyle class')
+ for type_ in mro:
+ # Find the class that owns the currently-executing method.
+ for meth in type_.__dict__.values():
+ # Drill down through any wrappers to the underlying func.
+ # This handles e.g. classmethod() and staticmethod().
+ try:
+ while not isinstance(meth, types.FunctionType):
+ if isinstance(meth, property):
+ # Calling __get__ on the property will invoke
+ # user code which might throw exceptions or
+ # have side effects
+ meth = meth.fget
+ else:
+ try:
+ meth = meth.__func__
+ except AttributeError:
+ meth = meth.__get__(type_or_obj, type_)
+ except (AttributeError, TypeError):
+ continue
+ if meth.func_code is f.f_code:
+ break # found
+ else:
+ # Not found. Move onto the next class in MRO.
+ continue
+ break # found
+ else:
+ raise RuntimeError('super() called outside a method')
+
+ # Dispatch to builtin super().
+ if type_or_obj is not _SENTINEL:
+ return _builtin_super(type_, type_or_obj)
+ return _builtin_super(type_)
+
+
+# --- exceptions
+
+
+if PY3:
+ FileNotFoundError = FileNotFoundError # NOQA
+ PermissionError = PermissionError # NOQA
+ ProcessLookupError = ProcessLookupError # NOQA
+ InterruptedError = InterruptedError # NOQA
+ ChildProcessError = ChildProcessError # NOQA
+ FileExistsError = FileExistsError # NOQA
+else:
+ # https://github.com/PythonCharmers/python-future/blob/exceptions/
+ # src/future/types/exceptions/pep3151.py
+ import platform
+
+ 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]
+ for attr in dir(unwrap_me):
+ if not attr.startswith('__'):
+ setattr(self, attr, getattr(unwrap_me, attr))
+ else:
+ super(TemporaryClass, self).__init__(*args, **kwargs)
+
+ class __metaclass__(type):
+ def __instancecheck__(cls, inst):
+ return instance_checker(inst)
+
+ def __subclasscheck__(cls, classinfo):
+ value = sys.exc_info()[1]
+ return isinstance(value, cls)
+
+ TemporaryClass.__name__ = instance_checker.__name__
+ TemporaryClass.__doc__ = instance_checker.__doc__
+ return TemporaryClass
+
+ return wrapped
+
+ @_instance_checking_exception(EnvironmentError)
+ def FileNotFoundError(inst):
+ return getattr(inst, 'errno', _SENTINEL) == errno.ENOENT
+
+ @_instance_checking_exception(EnvironmentError)
+ def ProcessLookupError(inst):
+ return getattr(inst, 'errno', _SENTINEL) == errno.ESRCH
+
+ @_instance_checking_exception(EnvironmentError)
+ def PermissionError(inst):
+ return getattr(inst, 'errno', _SENTINEL) in (
+ errno.EACCES, errno.EPERM)
+
+ @_instance_checking_exception(EnvironmentError)
+ def InterruptedError(inst):
+ return getattr(inst, 'errno', _SENTINEL) == errno.EINTR
+
+ @_instance_checking_exception(EnvironmentError)
+ def ChildProcessError(inst):
+ return getattr(inst, 'errno', _SENTINEL) == errno.ECHILD
+
+ @_instance_checking_exception(EnvironmentError)
+ def FileExistsError(inst):
+ return getattr(inst, 'errno', _SENTINEL) == errno.EEXIST
+
+ if platform.python_implementation() != "CPython":
+ try:
+ raise OSError(errno.EEXIST, "perm")
+ except FileExistsError:
+ pass
+ except OSError:
+ raise RuntimeError(
+ "broken or incompatible Python implementation, see: "
+ "https://github.com/giampaolo/psutil/issues/1659")
+
+
+# --- stdlib additions
+
+
+# py 3.2 functools.lru_cache
+# Taken from: http://code.activestate.com/recipes/578078
+# Credit: Raymond Hettinger
+try:
+ from functools import lru_cache
+except ImportError:
+ try:
+ from threading import RLock
+ except ImportError:
+ from dummy_threading import RLock
+
+ _CacheInfo = collections.namedtuple(
+ "CacheInfo", ["hits", "misses", "maxsize", "currsize"])
+
+ class _HashedSeq(list):
+ __slots__ = 'hashvalue'
+
+ def __init__(self, tup, hash=hash):
+ self[:] = tup
+ self.hashvalue = hash(tup)
+
+ 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):
+ key = args
+ if kwds:
+ sorted_items = sorted(kwds.items())
+ key += kwd_mark
+ for item in sorted_items:
+ key += item
+ if typed:
+ key += tuple(type(v) for v in args)
+ if kwds:
+ key += tuple(type(v) for k, v in sorted_items)
+ elif len(key) == 1 and type(key[0]) in fasttypes:
+ return key[0]
+ return _HashedSeq(key)
+
+ def lru_cache(maxsize=100, typed=False):
+ """Least-recently-used cache decorator, see:
+ http://docs.python.org/3/library/functools.html#functools.lru_cache
+ """
+ def decorating_function(user_function):
+ cache = dict()
+ stats = [0, 0]
+ HITS, MISSES = 0, 1
+ make_key = _make_key
+ cache_get = cache.get
+ _len = len
+ lock = RLock()
+ root = []
+ root[:] = [root, root, None, None]
+ 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)
+ if result is not root:
+ stats[HITS] += 1
+ return result
+ result = user_function(*args, **kwds)
+ cache[key] = result
+ stats[MISSES] += 1
+ return result
+ else:
+ def wrapper(*args, **kwds):
+ if kwds or typed:
+ key = make_key(args, kwds, typed)
+ else:
+ key = args
+ lock.acquire()
+ try:
+ link = cache_get(key)
+ if link is not None:
+ root, = nonlocal_root
+ link_prev, link_next, key, result = link
+ link_prev[NEXT] = link_next
+ link_next[PREV] = link_prev
+ last = root[PREV]
+ last[NEXT] = root[PREV] = link
+ link[PREV] = last
+ link[NEXT] = root
+ stats[HITS] += 1
+ return result
+ finally:
+ lock.release()
+ result = user_function(*args, **kwds)
+ lock.acquire()
+ try:
+ root, = nonlocal_root
+ if key in cache:
+ pass
+ elif _len(cache) >= maxsize:
+ oldroot = root
+ oldroot[KEY] = key
+ oldroot[RESULT] = result
+ root = nonlocal_root[0] = oldroot[NEXT]
+ oldkey = root[KEY]
+ root[KEY] = root[RESULT] = None
+ del cache[oldkey]
+ cache[key] = oldroot
+ else:
+ last = root[PREV]
+ link = [last, root, key, result]
+ last[NEXT] = root[PREV] = cache[key] = link
+ stats[MISSES] += 1
+ finally:
+ lock.release()
+ return result
+
+ def cache_info():
+ """Report cache statistics"""
+ lock.acquire()
+ try:
+ return _CacheInfo(stats[HITS], stats[MISSES], maxsize,
+ len(cache))
+ finally:
+ lock.release()
+
+ def cache_clear():
+ """Clear the cache and cache statistics"""
+ lock.acquire()
+ try:
+ cache.clear()
+ root = nonlocal_root[0]
+ root[:] = [root, root, None, None]
+ stats[:] = [0, 0]
+ finally:
+ lock.release()
+
+ wrapper.__wrapped__ = user_function
+ wrapper.cache_info = cache_info
+ wrapper.cache_clear = cache_clear
+ return functools.update_wrapper(wrapper, user_function)
+
+ return decorating_function
+
+
+# python 3.3
+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
+ file.
+
+ `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
+ 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))
+
+ if os.path.dirname(cmd):
+ if _access_check(cmd, mode):
+ return cmd
+ return None
+
+ if path is None:
+ path = os.environ.get("PATH", os.defpath)
+ if not path:
+ return None
+ path = path.split(os.pathsep)
+
+ if sys.platform == "win32":
+ if os.curdir not in path:
+ path.insert(0, os.curdir)
+
+ pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
+ if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
+ files = [cmd]
+ else:
+ files = [cmd + ext for ext in pathext]
+ else:
+ files = [cmd]
+
+ seen = set()
+ for dir in path:
+ normdir = os.path.normcase(dir)
+ if normdir not in seen:
+ seen.add(normdir)
+ for thefile in files:
+ name = os.path.join(dir, thefile)
+ if _access_check(name, mode):
+ return name
+ return None
+
+
+# python 3.3
+try:
+ from shutil import get_terminal_size
+except ImportError:
+ def get_terminal_size(fallback=(80, 24)):
+ try:
+ import fcntl
+ import termios
+ import struct
+ except ImportError:
+ return fallback
+ else:
+ try:
+ # This should work on Linux.
+ res = struct.unpack(
+ 'hh', fcntl.ioctl(1, termios.TIOCGWINSZ, '1234'))
+ return (res[1], res[0])
+ except Exception:
+ return fallback
diff --git a/contrib/python/psutil/py3/psutil/_pslinux.py b/contrib/python/psutil/py3/psutil/_pslinux.py
new file mode 100644
index 0000000000..c1dc52dd5f
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_pslinux.py
@@ -0,0 +1,2157 @@
+# 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.
+
+"""Linux platform implementation."""
+
+from __future__ import division
+
+import base64
+import collections
+import errno
+import functools
+import glob
+import os
+import re
+import socket
+import struct
+import sys
+import traceback
+import warnings
+from collections import defaultdict
+from collections import namedtuple
+
+from . import _common
+from . import _psposix
+from . import _psutil_linux as cext
+from . import _psutil_posix as cext_posix
+from ._common import AccessDenied
+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 FileNotFoundError
+from ._compat import PermissionError
+from ._compat import ProcessLookupError
+from ._compat import PY3
+
+if sys.version_info >= (3, 4):
+ import enum
+else:
+ enum = None
+
+
+__extra__all__ = [
+ #
+ 'PROCFS_PATH',
+ # io prio constants
+ "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE",
+ "IOPRIO_CLASS_IDLE",
+ # 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", ]
+
+
+# =====================================================================
+# --- globals
+# =====================================================================
+
+
+POWER_SUPPLY_PATH = "/sys/class/power_supply"
+HAS_SMAPS = os.path.exists('/proc/%s/smaps' % 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
+# a size of 512 bytes. Despite this value can be queried at runtime
+# via /sys/block/{DISK}/queue/hw_sector_size and results may vary
+# between 1k, 2k, or 4k... 512 appears to be a magic constant used
+# throughout Linux source code:
+# * https://stackoverflow.com/a/38136179/376587
+# * https://lists.gt.net/linux/kernel/2241060
+# * https://github.com/giampaolo/psutil/issues/1305
+# * https://github.com/torvalds/linux/blob/
+# 4f671fe2f9523a1ea206f63fe60a7c7b3a56d5c7/include/linux/bio.h#L99
+# * https://lkml.org/lkml/2015/8/17/234
+DISK_SECTOR_SIZE = 512
+
+if enum is None:
+ AF_LINK = socket.AF_PACKET
+else:
+ 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
+if enum is None:
+ IOPRIO_CLASS_NONE = 0
+ IOPRIO_CLASS_RT = 1
+ IOPRIO_CLASS_BE = 2
+ IOPRIO_CLASS_IDLE = 3
+else:
+ class IOPriority(enum.IntEnum):
+ IOPRIO_CLASS_NONE = 0
+ IOPRIO_CLASS_RT = 1
+ IOPRIO_CLASS_BE = 2
+ IOPRIO_CLASS_IDLE = 3
+
+ globals().update(IOPriority.__members__)
+
+# See:
+# https://github.com/torvalds/linux/blame/master/fs/proc/array.c
+# ...and (TASK_* constants):
+# https://github.com/torvalds/linux/blob/master/include/linux/sched.h
+PROC_STATUSES = {
+ "R": _common.STATUS_RUNNING,
+ "S": _common.STATUS_SLEEPING,
+ "D": _common.STATUS_DISK_SLEEP,
+ "T": _common.STATUS_STOPPED,
+ "t": _common.STATUS_TRACING_STOP,
+ "Z": _common.STATUS_ZOMBIE,
+ "X": _common.STATUS_DEAD,
+ "x": _common.STATUS_DEAD,
+ "K": _common.STATUS_WAKE_KILL,
+ "W": _common.STATUS_WAKING,
+ "I": _common.STATUS_IDLE,
+ "P": _common.STATUS_PARKED,
+}
+
+# https://github.com/torvalds/linux/blob/master/include/net/tcp_states.h
+TCP_STATUSES = {
+ "01": _common.CONN_ESTABLISHED,
+ "02": _common.CONN_SYN_SENT,
+ "03": _common.CONN_SYN_RECV,
+ "04": _common.CONN_FIN_WAIT1,
+ "05": _common.CONN_FIN_WAIT2,
+ "06": _common.CONN_TIME_WAIT,
+ "07": _common.CONN_CLOSE,
+ "08": _common.CONN_CLOSE_WAIT,
+ "09": _common.CONN_LAST_ACK,
+ "0A": _common.CONN_LISTEN,
+ "0B": _common.CONN_CLOSING
+}
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.virtual_memory()
+svmem = namedtuple(
+ 'svmem', ['total', 'available', 'percent', 'used', 'free',
+ 'active', 'inactive', 'buffers', 'cached', 'shared', 'slab'])
+# psutil.disk_io_counters()
+sdiskio = namedtuple(
+ 'sdiskio', ['read_count', 'write_count',
+ 'read_bytes', 'write_bytes',
+ 'read_time', 'write_time',
+ 'read_merged_count', 'write_merged_count',
+ 'busy_time'])
+# psutil.Process().open_files()
+popenfile = namedtuple(
+ 'popenfile', ['path', 'fd', 'position', 'mode', 'flags'])
+# psutil.Process().memory_info()
+pmem = namedtuple('pmem', 'rss vms shared text lib data dirty')
+# psutil.Process().memory_full_info()
+pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', 'pss', 'swap'))
+# psutil.Process().memory_maps(grouped=True)
+pmmap_grouped = namedtuple(
+ 'pmmap_grouped',
+ ['path', 'rss', 'size', 'pss', 'shared_clean', 'shared_dirty',
+ 'private_clean', 'private_dirty', 'referenced', 'anonymous', 'swap'])
+# psutil.Process().memory_maps(grouped=False)
+pmmap_ext = namedtuple(
+ 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
+# psutil.Process.io_counters()
+pio = namedtuple('pio', ['read_count', 'write_count',
+ 'read_bytes', 'write_bytes',
+ 'read_chars', 'write_chars'])
+# psutil.Process.cpu_times()
+pcputimes = namedtuple('pcputimes',
+ ['user', 'system', 'children_user', 'children_system',
+ 'iowait'])
+
+
+# =====================================================================
+# --- utils
+# =====================================================================
+
+
+def readlink(path):
+ """Wrapper around os.readlink()."""
+ assert isinstance(path, basestring), path
+ path = os.readlink(path)
+ # readlink() might return paths containing null bytes ('\x00')
+ # resulting in "TypeError: must be encoded string without NULL
+ # bytes, not str" errors when the string is passed to other
+ # fs-related functions (os.*, open(), ...).
+ # Apparently everything after '\x00' is garbage (we can have
+ # ' (deleted)', 'new' and possibly others), see:
+ # https://github.com/giampaolo/psutil/issues/717
+ path = path.split('\x00')[0]
+ # Certain paths have ' (deleted)' appended. Usually this is
+ # bogus as the file actually exists. Even if it doesn't we
+ # don't care.
+ if path.endswith(' (deleted)') and not path_exists_strict(path):
+ path = path[:-10]
+ return path
+
+
+def file_flags_to_mode(flags):
+ """Convert file's open() flags into a readable string.
+ Used by Process.open_files().
+ """
+ modes_map = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
+ mode = modes_map[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
+ if flags & os.O_APPEND:
+ mode = mode.replace('w', 'a', 1)
+ mode = mode.replace('w+', 'r+')
+ # possible values: r, w, a, r+, a+
+ return mode
+
+
+def is_storage_device(name):
+ """Return True if the given name refers to a root device (e.g.
+ "sda", "nvme0n1") as opposed to a logical partition (e.g. "sda1",
+ "nvme0n1p1"). If name is a virtual device (e.g. "loop1", "ram")
+ return True.
+ """
+ # Readapted 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...).
+ name = name.replace('/', '!')
+ including_virtual = True
+ if including_virtual:
+ path = "/sys/block/%s" % name
+ else:
+ path = "/sys/block/%s/device" % name
+ return os.access(path, os.F_OK)
+
+
+@memoize
+def set_scputimes_ntuple(procfs_path):
+ """Set a namedtuple of variable fields depending on the CPU times
+ available on this Linux kernel version which may be:
+ (user, nice, system, idle, iowait, irq, softirq, [steal, [guest,
+ [guest_nice]]])
+ Used by cpu_times() function.
+ """
+ global scputimes
+ with open_binary('%s/stat' % procfs_path) as f:
+ values = f.readline().split()[1:]
+ fields = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq']
+ vlen = len(values)
+ if vlen >= 8:
+ # Linux >= 2.6.11
+ fields.append('steal')
+ if vlen >= 9:
+ # Linux >= 2.6.24
+ fields.append('guest')
+ if vlen >= 10:
+ # Linux >= 3.2.0
+ fields.append('guest_nice')
+ 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
+ # Don't want to crash at import time.
+ traceback.print_exc()
+ scputimes = namedtuple('scputimes', 'user system idle')(0.0, 0.0, 0.0)
+
+
+# =====================================================================
+# --- prlimit
+# =====================================================================
+
+# Backport of resource.prlimit() for Python 2. Originally this was done
+# in C, but CentOS-6 which we use to create manylinux wheels is too old
+# and does not support prlimit() syscall. As such the resulting wheel
+# would not include prlimit(), even when installed on newer systems.
+# This is the only part of psutil using ctypes.
+
+prlimit = None
+try:
+ from resource import prlimit # python >= 3.4
+except ImportError:
+ import ctypes
+
+ try:
+ libc = ctypes.CDLL(None, use_errno=True)
+ except:
+ libc = None
+
+ if hasattr(libc, "prlimit"):
+
+ def prlimit(pid, resource_, limits=None):
+ class StructRlimit(ctypes.Structure):
+ _fields_ = [('rlim_cur', ctypes.c_longlong),
+ ('rlim_max', ctypes.c_longlong)]
+
+ current = StructRlimit()
+ if limits is None:
+ # get
+ ret = libc.prlimit(pid, resource_, None, ctypes.byref(current))
+ else:
+ # set
+ new = StructRlimit()
+ new.rlim_cur = limits[0]
+ new.rlim_max = limits[1]
+ ret = libc.prlimit(
+ pid, resource_, ctypes.byref(new), ctypes.byref(current))
+
+ if ret != 0:
+ 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()])
+
+
+# =====================================================================
+# --- system memory
+# =====================================================================
+
+
+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/
+ 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
+ It is still way more realistic than doing (free + cached) though.
+ """
+ # Fallback for very old distros. 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
+ 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:
+ return fallback
+ try:
+ f = open_binary('%s/zoneinfo' % get_procfs_path())
+ except IOError:
+ return fallback # kernel 2.6.13
+
+ watermark_low = 0
+ with f:
+ for line in f:
+ line = line.strip()
+ if line.startswith(b'low'):
+ watermark_low += int(line.split()[1])
+ watermark_low *= PAGESIZE
+
+ avail = free - watermark_low
+ pagecache = lru_active_file + lru_inactive_file
+ pagecache -= min(pagecache / 2, watermark_low)
+ avail += pagecache
+ avail += slab_reclaimable - min(slab_reclaimable / 2.0, watermark_low)
+ return int(avail)
+
+
+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):
+ 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".
+ """
+ missing_fields = []
+ mems = {}
+ with open_binary('%s/meminfo' % get_procfs_path()) as f:
+ for line in f:
+ fields = line.split()
+ mems[fields[0]] = int(fields[1]) * 1024
+
+ # /proc doc states that the available fields in /proc/meminfo vary
+ # by architecture and compile options, but these 3 values are also
+ # returned by sysinfo(2); as such we assume they are always there.
+ total = mems[b'MemTotal:']
+ free = mems[b'MemFree:']
+ try:
+ buffers = mems[b'Buffers:']
+ except KeyError:
+ # https://github.com/giampaolo/psutil/issues/1010
+ buffers = 0
+ missing_fields.append('buffers')
+ try:
+ cached = mems[b"Cached:"]
+ except KeyError:
+ cached = 0
+ missing_fields.append('cached')
+ else:
+ # "free" cmdline utility sums reclaimable to cached.
+ # Older versions of procps used to add slab memory instead.
+ # This got changed in:
+ # https://gitlab.com/procps-ng/procps/commit/
+ # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e
+ cached += mems.get(b"SReclaimable:", 0) # since kernel 2.6.19
+
+ try:
+ shared = mems[b'Shmem:'] # since kernel 2.6.32
+ except KeyError:
+ try:
+ shared = mems[b'MemShared:'] # kernels 2.4
+ except KeyError:
+ shared = 0
+ missing_fields.append('shared')
+
+ try:
+ active = mems[b"Active:"]
+ except KeyError:
+ active = 0
+ missing_fields.append('active')
+
+ try:
+ inactive = mems[b"Inactive:"]
+ except KeyError:
+ try:
+ inactive = \
+ mems[b"Inact_dirty:"] + \
+ mems[b"Inact_clean:"] + \
+ mems[b"Inact_laundry:"]
+ except KeyError:
+ inactive = 0
+ missing_fields.append('inactive')
+
+ try:
+ slab = mems[b"Slab:"]
+ except KeyError:
+ slab = 0
+
+ used = total - free - cached - buffers
+ if used < 0:
+ # May be symptomatic of running within a LCX container where such
+ # values will be dramatically distorted over those of the host.
+ used = total - free
+
+ # - starting from 4.4.0 we match free's "available" column.
+ # Before 4.4.0 we calculated it as (free + buffers + cached)
+ # which matched htop.
+ # - free and htop available memory differs as per:
+ # http://askubuntu.com/a/369589
+ # http://unix.stackexchange.com/a/65852/168884
+ # - MemAvailable has been introduced in kernel 3.14
+ try:
+ avail = mems[b'MemAvailable:']
+ except KeyError:
+ 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:
+ avail = free
+
+ percent = usage_percent((total - avail), total, round_=1)
+
+ # Warn about missing metrics which are set to 0.
+ 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)
+
+
+def swap_memory():
+ """Return swap memory metrics."""
+ mems = {}
+ with open_binary('%s/meminfo' % get_procfs_path()) as f:
+ for line in f:
+ fields = line.split()
+ mems[fields[0]] = int(fields[1]) * 1024
+ # We prefer /proc/meminfo over sysinfo() syscall so that
+ # psutil.PROCFS_PATH can be used in order to allow retrieval
+ # for linux containers, see:
+ # https://github.com/giampaolo/psutil/issues/1015
+ try:
+ total = mems[b'SwapTotal:']
+ free = mems[b'SwapFree:']
+ except KeyError:
+ _, _, _, _, total, free, unit_multiplier = cext.linux_sysinfo()
+ total *= unit_multiplier
+ free *= unit_multiplier
+
+ used = total - free
+ percent = usage_percent(used, total, round_=1)
+ # get pgin/pgouts
+ try:
+ 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)
+ sin = sout = 0
+ else:
+ with f:
+ sin = sout = None
+ for line in f:
+ # values are expressed in 4 kilo bytes, we want
+ # bytes instead
+ if line.startswith(b'pswpin'):
+ sin = int(line.split(b' ')[1]) * 4 * 1024
+ elif line.startswith(b'pswpout'):
+ sout = int(line.split(b' ')[1]) * 4 * 1024
+ if sin is not None and sout is not None:
+ break
+ else:
+ # 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)
+ sin = sout = 0
+ return _common.sswap(total, used, free, percent, sin, sout)
+
+
+# =====================================================================
+# --- CPU
+# =====================================================================
+
+
+def cpu_times():
+ """Return a named tuple representing the following system-wide
+ CPU times:
+ (user, nice, system, idle, iowait, irq, softirq [steal, [guest,
+ [guest_nice]]])
+ Last 3 fields may not be available on all Linux kernel versions.
+ """
+ procfs_path = get_procfs_path()
+ 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 = [float(x) / CLOCK_TICKS for x in fields]
+ return scputimes(*fields)
+
+
+def per_cpu_times():
+ """Return a list of namedtuple representing the CPU times
+ for every CPU available on the system.
+ """
+ procfs_path = get_procfs_path()
+ set_scputimes_ntuple(procfs_path)
+ cpus = []
+ with open_binary('%s/stat' % procfs_path) as f:
+ # get rid of the first line which refers to system wide CPU stats
+ f.readline()
+ for line in f:
+ if line.startswith(b'cpu'):
+ values = line.split()
+ fields = values[1:len(scputimes._fields) + 1]
+ fields = [float(x) / CLOCK_TICKS for x in fields]
+ entry = scputimes(*fields)
+ cpus.append(entry)
+ return cpus
+
+
+def cpu_count_logical():
+ """Return the number of logical CPUs in the system."""
+ try:
+ return os.sysconf("SC_NPROCESSORS_ONLN")
+ except ValueError:
+ # as a second fallback we try to parse /proc/cpuinfo
+ num = 0
+ with open_binary('%s/cpuinfo' % get_procfs_path()) as f:
+ for line in f:
+ if line.lower().startswith(b'processor'):
+ num += 1
+
+ # unknown format (e.g. amrel/sparc architectures), see:
+ # https://github.com/giampaolo/psutil/issues/200
+ # try to parse /proc/stat as a last resort
+ if num == 0:
+ search = re.compile(r'cpu\d')
+ with open_text('%s/stat' % get_procfs_path()) as f:
+ for line in f:
+ line = line.split(' ')[0]
+ if search.match(line):
+ num += 1
+
+ if num == 0:
+ # mimic os.cpu_count()
+ return None
+ return num
+
+
+def cpu_count_physical():
+ """Return the number of physical cores in the system."""
+ # Method #1
+ ls = set()
+ # These 2 files are the same but */core_cpus_list is newer while
+ # */thread_siblings_list is deprecated and may disappear in the future.
+ # https://www.kernel.org/doc/Documentation/admin-guide/cputopology.rst
+ # https://github.com/giampaolo/psutil/pull/1727#issuecomment-707624964
+ # https://lkml.org/lkml/2019/2/26/41
+ p1 = "/sys/devices/system/cpu/cpu[0-9]*/topology/core_cpus_list"
+ p2 = "/sys/devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list"
+ for path in glob.glob(p1) or glob.glob(p2):
+ with open_binary(path) as f:
+ ls.add(f.read().strip())
+ result = len(ls)
+ if result != 0:
+ return result
+
+ # Method #2
+ mapping = {}
+ current_info = {}
+ with open_binary('%s/cpuinfo' % get_procfs_path()) as f:
+ for line in f:
+ line = line.strip().lower()
+ if not line:
+ # new section
+ try:
+ mapping[current_info[b'physical id']] = \
+ current_info[b'cpu cores']
+ except KeyError:
+ pass
+ current_info = {}
+ else:
+ # ongoing section
+ if line.startswith((b'physical id', b'cpu cores')):
+ key, value = line.split(b'\t:', 1)
+ current_info[key] = int(value)
+
+ result = sum(mapping.values())
+ return result or None # mimic os.cpu_count()
+
+
+def cpu_stats():
+ """Return various CPU stats as a named tuple."""
+ with open_binary('%s/stat' % get_procfs_path()) as f:
+ ctx_switches = None
+ interrupts = None
+ soft_interrupts = None
+ for line in f:
+ if line.startswith(b'ctxt'):
+ ctx_switches = int(line.split()[1])
+ elif line.startswith(b'intr'):
+ 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:
+ break
+ syscalls = 0
+ return _common.scpustats(
+ ctx_switches, interrupts, soft_interrupts, syscalls)
+
+
+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
+
+ 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)
+ 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)
+ if curr is None:
+ raise NotImplementedError(
+ "can't find current frequency file")
+ curr = int(curr) / 1000
+ max_ = int(cat(pjoin(path, "scaling_max_freq"))) / 1000
+ min_ = int(cat(pjoin(path, "scaling_min_freq"))) / 1000
+ ret.append(_common.scpufreq(curr, min_, max_))
+ return ret
+
+elif os.path.exists("/proc/cpuinfo"):
+ 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 []
+
+
+# =====================================================================
+# --- network
+# =====================================================================
+
+
+net_if_addrs = cext_posix.net_if_addrs
+
+
+class _Ipv6UnsupportedError(Exception):
+ pass
+
+
+class Connections:
+ """A wrapper on top of /proc/net/* files, retrieving per-process
+ and system-wide open connections (TCP, UDP, UNIX) similarly to
+ "netstat -an".
+
+ Note: in case of UNIX sockets we're only able to determine the
+ local endpoint/path, not the one it's connected to.
+ According to [1] it would be possible but not easily.
+
+ [1] http://serverfault.com/a/417946
+ """
+
+ def __init__(self):
+ # The string represents the basename of the corresponding
+ # /proc/net/{proto_name} file.
+ tcp4 = ("tcp", socket.AF_INET, socket.SOCK_STREAM)
+ tcp6 = ("tcp6", socket.AF_INET6, socket.SOCK_STREAM)
+ udp4 = ("udp", socket.AF_INET, socket.SOCK_DGRAM)
+ udp6 = ("udp6", socket.AF_INET6, socket.SOCK_DGRAM)
+ unix = ("unix", socket.AF_UNIX, None)
+ self.tmap = {
+ "all": (tcp4, tcp6, udp4, udp6, unix),
+ "tcp": (tcp4, tcp6),
+ "tcp4": (tcp4,),
+ "tcp6": (tcp6,),
+ "udp": (udp4, udp6),
+ "udp4": (udp4,),
+ "udp6": (udp6,),
+ "unix": (unix,),
+ "inet": (tcp4, tcp6, udp4, udp6),
+ "inet4": (tcp4, udp4),
+ "inet6": (tcp6, udp6),
+ }
+ self._procfs_path = None
+
+ def get_proc_inodes(self, pid):
+ inodes = defaultdict(list)
+ for fd in os.listdir("%s/%s/fd" % (self._procfs_path, pid)):
+ try:
+ inode = readlink("%s/%s/fd/%s" % (self._procfs_path, pid, fd))
+ except (FileNotFoundError, ProcessLookupError):
+ # ENOENT == file which is gone in the meantime;
+ # os.stat('/proc/%s' % self.pid) will be done later
+ # to force NSP (if it's the case)
+ continue
+ except OSError as err:
+ if err.errno == errno.EINVAL:
+ # not a link
+ continue
+ raise
+ else:
+ if inode.startswith('socket:['):
+ # the process is using a socket
+ inode = inode[8:][:-1]
+ inodes[inode].append((pid, int(fd)))
+ return inodes
+
+ def get_all_inodes(self):
+ inodes = {}
+ for pid in pids():
+ try:
+ inodes.update(self.get_proc_inodes(pid))
+ except (FileNotFoundError, ProcessLookupError, PermissionError):
+ # os.listdir() is gonna raise a lot of access denied
+ # exceptions in case of unprivileged user; that's fine
+ # as we'll just end up returning a connection with PID
+ # and fd set to None anyway.
+ # Both netstat -an and lsof does the same so it's
+ # unlikely we can do any better.
+ # ENOENT just means a PID disappeared on us.
+ continue
+ return inodes
+
+ @staticmethod
+ def decode_address(addr, family):
+ """Accept an "ip:port" address as displayed in /proc/net/*
+ and convert it into a human readable form, like:
+
+ "0500000A:0016" -> ("10.0.0.5", 22)
+ "0000000000000000FFFF00000100007F:9E49" -> ("::ffff:127.0.0.1", 40521)
+
+ The IP address portion is a little or big endian four-byte
+ hexadecimal number; that is, the least significant byte is listed
+ first, so we need to reverse the order of the bytes to convert it
+ to an IP address.
+ The port is represented as a two-byte hexadecimal number.
+
+ Reference:
+ http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html
+ """
+ ip, port = addr.split(':')
+ port = int(port, 16)
+ # this usually refers to a local socket in listen mode with
+ # no end-points connected
+ if not port:
+ return ()
+ if PY3:
+ ip = ip.encode('ascii')
+ if family == socket.AF_INET:
+ # see: https://github.com/giampaolo/psutil/issues/201
+ if LITTLE_ENDIAN:
+ ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1])
+ else:
+ ip = socket.inet_ntop(family, base64.b16decode(ip))
+ else: # IPv6
+ ip = base64.b16decode(ip)
+ try:
+ # see: https://github.com/giampaolo/psutil/issues/201
+ if LITTLE_ENDIAN:
+ ip = socket.inet_ntop(
+ socket.AF_INET6,
+ struct.pack('>4I', *struct.unpack('<4I', ip)))
+ else:
+ ip = socket.inet_ntop(
+ socket.AF_INET6,
+ struct.pack('<4I', *struct.unpack('<4I', ip)))
+ except ValueError:
+ # see: https://github.com/giampaolo/psutil/issues/623
+ if not supports_ipv6():
+ raise _Ipv6UnsupportedError
+ else:
+ raise
+ return _common.addr(ip, port)
+
+ @staticmethod
+ def process_inet(file, family, type_, inodes, filter_pid=None):
+ """Parse /proc/net/tcp* and /proc/net/udp* files."""
+ if file.endswith('6') and not os.path.exists(file):
+ # IPv6 not supported
+ return
+ with open_text(file, buffering=BIGFILE_BUFFERING) as f:
+ f.readline() # skip the first line
+ for lineno, line in enumerate(f, 1):
+ try:
+ _, laddr, raddr, status, _, _, _, _, _, inode = \
+ line.split()[:10]
+ except ValueError:
+ raise RuntimeError(
+ "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 "
+ # "PIDs references")
+ pid, fd = inodes[inode][0]
+ else:
+ pid, fd = None, -1
+ if filter_pid is not None and filter_pid != pid:
+ continue
+ else:
+ if type_ == socket.SOCK_STREAM:
+ status = TCP_STATUSES[status]
+ else:
+ status = _common.CONN_NONE
+ try:
+ laddr = Connections.decode_address(laddr, family)
+ raddr = Connections.decode_address(raddr, family)
+ except _Ipv6UnsupportedError:
+ continue
+ yield (fd, family, type_, laddr, raddr, status, pid)
+
+ @staticmethod
+ def process_unix(file, family, inodes, filter_pid=None):
+ """Parse /proc/net/unix files."""
+ with open_text(file, buffering=BIGFILE_BUFFERING) as f:
+ f.readline() # skip the first line
+ for line in f:
+ tokens = line.split()
+ try:
+ _, _, _, _, type_, _, inode = tokens[0:7]
+ except ValueError:
+ if ' ' not in line:
+ # see: https://github.com/giampaolo/psutil/issues/766
+ continue
+ raise RuntimeError(
+ "error while parsing %s; malformed line %r" % (
+ file, line))
+ if inode in inodes:
+ # With UNIX sockets we can have a single inode
+ # referencing many file descriptors.
+ pairs = inodes[inode]
+ else:
+ pairs = [(None, -1)]
+ for pid, fd in pairs:
+ if filter_pid is not None and filter_pid != pid:
+ continue
+ else:
+ if len(tokens) == 8:
+ path = tokens[-1]
+ else:
+ path = ""
+ type_ = _common.socktype_to_enum(int(type_))
+ # XXX: determining the remote endpoint of a
+ # UNIX socket on Linux is not possible, see:
+ # https://serverfault.com/questions/252723/
+ raddr = ""
+ status = _common.CONN_NONE
+ yield (fd, family, type_, path, raddr, status, pid)
+
+ 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])))
+ self._procfs_path = get_procfs_path()
+ if pid is not None:
+ inodes = self.get_proc_inodes(pid)
+ if not inodes:
+ # no connections for this process
+ return []
+ else:
+ inodes = self.get_all_inodes()
+ ret = set()
+ for proto_name, family, type_ in self.tmap[kind]:
+ 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)
+ else:
+ 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)
+ else:
+ conn = _common.sconn(fd, family, type_, laddr, raddr,
+ status, bound_pid)
+ ret.add(conn)
+ return list(ret)
+
+
+_connections = Connections()
+
+
+def net_connections(kind='inet'):
+ """Return system-wide open connections."""
+ return _connections.retrieve(kind)
+
+
+def net_io_counters():
+ """Return network I/O statistics for every network interface
+ installed on the system as a dict of raw tuples.
+ """
+ with open_text("%s/net/dev" % get_procfs_path()) as f:
+ lines = f.readlines()
+ retdict = {}
+ for line in lines[2:]:
+ colon = line.rfind(':')
+ assert colon > 0, repr(line)
+ name = line[:colon].strip()
+ 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)
+ 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}
+ 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)
+ 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:
+ ret[name] = _common.snicstats(isup, duplex_map[duplex], speed, mtu)
+ return ret
+
+
+# =====================================================================
+# --- disks
+# =====================================================================
+
+
+disk_usage = _psposix.disk_usage
+
+
+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.
+ # On Linux 2.4 each line has always 15 fields, e.g.:
+ # "3 0 8 hda 8 8 8 8 8 8 8 8 8 8 8"
+ # On Linux 2.6+ each line *usually* has 14 fields, and the disk
+ # name is in another position, like this:
+ # "3 0 hda 8 8 8 8 8 8 8 8 8 8 8"
+ # ...unless (Linux 2.6) the line refers to a partition instead
+ # of a disk, in which case the line has less fields (7):
+ # "3 1 hda1 8 8 8 8"
+ # 4.18+ has 4 fields added:
+ # "3 0 hda 8 8 8 8 8 8 8 8 8 8 8 0 0 0 0"
+ # 5.5 has 2 more fields.
+ # See:
+ # https://www.kernel.org/doc/Documentation/iostats.txt
+ # https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
+ with open_text("%s/diskstats" % get_procfs_path()) as f:
+ lines = f.readlines()
+ for line in lines:
+ fields = line.split()
+ flen = len(fields)
+ if flen == 15:
+ # Linux 2.4
+ name = fields[3]
+ reads = int(fields[2])
+ (reads_merged, rbytes, rtime, writes, writes_merged,
+ wbytes, wtime, _, busy_time, _) = map(int, fields[4:14])
+ elif flen == 14 or flen >= 18:
+ # Linux 2.6+, line referring to a disk
+ name = fields[2]
+ (reads, reads_merged, rbytes, rtime, writes, writes_merged,
+ wbytes, wtime, _, busy_time, _) = map(int, fields[3:14])
+ elif flen == 7:
+ # Linux 2.6+, line referring to a partition
+ name = fields[2]
+ reads, rbytes, writes, wbytes = map(int, fields[3:])
+ rtime = wtime = reads_merged = writes_merged = busy_time = 0
+ else:
+ raise ValueError("not sure how to interpret line %r" % line)
+ yield (name, reads, writes, rbytes, wbytes, rtime, wtime,
+ reads_merged, writes_merged, busy_time)
+
+ def read_sysfs():
+ for block in os.listdir('/sys/block'):
+ for root, _, files in os.walk(os.path.join('/sys/block', block)):
+ if 'stat' not in files:
+ continue
+ with open_text(os.path.join(root, 'stat')) as f:
+ fields = f.read().strip().split()
+ name = os.path.basename(root)
+ (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)
+
+ if os.path.exists('%s/diskstats' % get_procfs_path()):
+ gen = read_procfs()
+ elif os.path.exists('/sys/block'):
+ gen = read_sysfs()
+ else:
+ raise NotImplementedError(
+ "%s/diskstats nor /sys/block filesystem are available on this "
+ "system" % get_procfs_path())
+
+ retdict = {}
+ for entry in gen:
+ (name, reads, writes, rbytes, wbytes, rtime, wtime, reads_merged,
+ writes_merged, busy_time) = entry
+ if not perdisk and not is_storage_device(name):
+ # perdisk=False means we want to calculate totals so we skip
+ # partitions (e.g. 'sda1', 'nvme0n1p1') and only include
+ # base disk devices (e.g. 'sda', 'nvme0n1'). Base disks
+ # include a total of all their partitions + some extra size
+ # of their own:
+ # $ cat /proc/diskstats
+ # 259 0 sda 10485760 ...
+ # 259 1 sda1 5186039 ...
+ # 259 1 sda2 5082039 ...
+ # See:
+ # https://github.com/giampaolo/psutil/pull/1313
+ continue
+
+ rbytes *= DISK_SECTOR_SIZE
+ wbytes *= DISK_SECTOR_SIZE
+ retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime,
+ reads_merged, writes_merged, busy_time)
+
+ return retdict
+
+
+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")
+
+ # See: https://github.com/giampaolo/psutil/issues/1307
+ if procfs_path == "/proc" and os.path.isfile('/etc/mtab'):
+ mounts_path = os.path.realpath("/etc/mtab")
+ else:
+ mounts_path = os.path.realpath("%s/self/mounts" % procfs_path)
+
+ retlist = []
+ partitions = cext.disk_partitions(mounts_path)
+ for partition in partitions:
+ device, mountpoint, fstype, opts = partition
+ if device == 'none':
+ 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)
+ retlist.append(ntuple)
+
+ return retlist
+
+
+# =====================================================================
+# --- sensors
+# =====================================================================
+
+
+def sensors_temperatures():
+ """Return hardware (CPU and others) temperatures as a dict
+ including hardware name, label, current, max and critical
+ temperatures.
+
+ Implementation notes:
+ - /sys/class/hwmon looks like the most recent interface to
+ retrieve this info, and this implementation relies on it
+ only (old distros will probably use something else)
+ - lm-sensors on Ubuntu 16.04 relies on /sys/class/hwmon
+ - /sys/class/thermal/thermal_zone* is another one but it's more
+ difficult to parse
+ """
+ ret = collections.defaultdict(list)
+ basenames = glob.glob('/sys/class/hwmon/hwmon*/temp*_*')
+ # CentOS has an intermediate /device directory:
+ # https://github.com/giampaolo/psutil/issues/971
+ # https://github.com/nicolargo/glances/issues/1060
+ basenames.extend(glob.glob('/sys/class/hwmon/hwmon*/device/temp*_*'))
+ basenames = sorted(set([x.split('_')[0] for x in basenames]))
+
+ # Only add the coretemp hwmon entries if they're not already in
+ # /sys/class/hwmon/
+ # https://github.com/giampaolo/psutil/issues/1708
+ # https://github.com/giampaolo/psutil/pull/1648
+ basenames2 = glob.glob(
+ '/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)
+ if altname not in basenames:
+ basenames.append(name)
+
+ for base in basenames:
+ try:
+ path = base + '_input'
+ current = float(cat(path)) / 1000.0
+ path = os.path.join(os.path.dirname(base), 'name')
+ unit_name = cat(path, binary=False)
+ 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
+ # is a stinky broken mess.
+ # https://github.com/giampaolo/psutil/issues/1009
+ # https://github.com/giampaolo/psutil/issues/1101
+ # https://github.com/giampaolo/psutil/issues/1129
+ # https://github.com/giampaolo/psutil/issues/1245
+ # 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)
+
+ if high is not None:
+ try:
+ high = float(high) / 1000.0
+ except ValueError:
+ high = None
+ if critical is not None:
+ try:
+ critical = float(critical) / 1000.0
+ except ValueError:
+ critical = None
+
+ ret[unit_name].append((label, current, high, critical))
+
+ # Indication that no sensors were detected in /sys/class/hwmon/
+ if not basenames:
+ basenames = glob.glob('/sys/class/thermal/thermal_zone*')
+ basenames = sorted(set(basenames))
+
+ for base in basenames:
+ try:
+ path = os.path.join(base, 'temp')
+ current = float(cat(path)) / 1000.0
+ path = os.path.join(base, 'type')
+ unit_name = cat(path, binary=False)
+ except (IOError, OSError, ValueError) as err:
+ debug("ignoring %r for file %r" % (err, path))
+ continue
+
+ trip_paths = glob.glob(base + '/trip_point*')
+ 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)
+ if trip_type == 'critical':
+ critical = cat(os.path.join(base, trip_point + "_temp"),
+ fallback=None)
+ elif trip_type == 'high':
+ high = cat(os.path.join(base, trip_point + "_temp"),
+ fallback=None)
+
+ if high is not None:
+ try:
+ high = float(high) / 1000.0
+ except ValueError:
+ high = None
+ if critical is not None:
+ try:
+ critical = float(critical) / 1000.0
+ except ValueError:
+ critical = None
+
+ ret[unit_name].append(('', current, high, critical))
+
+ return dict(ret)
+
+
+def sensors_fans():
+ """Return hardware fans info (for CPU and other peripherals) as a
+ dict including hardware label and current speed.
+
+ Implementation notes:
+ - /sys/class/hwmon looks like the most recent interface to
+ retrieve this info, and this implementation relies on it
+ only (old distros will probably use something else)
+ - lm-sensors on Ubuntu 16.04 relies on /sys/class/hwmon
+ """
+ ret = collections.defaultdict(list)
+ basenames = glob.glob('/sys/class/hwmon/hwmon*/fan*_*')
+ if not basenames:
+ # CentOS has an intermediate /device directory:
+ # https://github.com/giampaolo/psutil/issues/971
+ basenames = glob.glob('/sys/class/hwmon/hwmon*/device/fan*_*')
+
+ basenames = sorted(set([x.split('_')[0] for x in basenames]))
+ for base in basenames:
+ try:
+ current = int(cat(base + '_input'))
+ except (IOError, OSError) as err:
+ warnings.warn("ignoring %r" % err, RuntimeWarning)
+ continue
+ unit_name = cat(os.path.join(os.path.dirname(base), 'name'),
+ binary=False)
+ label = cat(base + '_label', fallback='', binary=False)
+ ret[unit_name].append(_common.sfan(label, current))
+
+ return dict(ret)
+
+
+def sensors_battery():
+ """Return battery information.
+ 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
+ """
+ null = object()
+
+ def multi_cat(*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)
+ if ret != null:
+ return int(ret) if ret.isdigit() else ret
+ return None
+
+ 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
+ # some rare exceptions:
+ # https://github.com/giampaolo/psutil/issues/1238
+ 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")
+
+ # Percent. If we have energy_full the percentage will be more
+ # accurate compared to reading /capacity file (float vs. int).
+ if energy_full is not None and energy_now is not None:
+ try:
+ percent = 100.0 * energy_now / energy_full
+ except ZeroDivisionError:
+ percent = 0.0
+ else:
+ percent = int(cat(root + "/capacity", fallback=-1))
+ if percent == -1:
+ return None
+
+ # Is AC power cable plugged in?
+ # Note: AC0 is not always available and sometimes (e.g. CentOS7)
+ # it's called "AC".
+ power_plugged = None
+ online = multi_cat(
+ os.path.join(POWER_SUPPLY_PATH, "AC0/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()
+ if status == "discharging":
+ power_plugged = False
+ elif status in ("charging", "full"):
+ power_plugged = True
+
+ # Seconds left.
+ # Note to self: we may also calculate the charging ETA as per:
+ # https://github.com/thialfihar/dotfiles/blob/
+ # 013937745fd9050c30146290e8f963d65c0179e6/bin/battery.py#L55
+ if power_plugged:
+ secsleft = _common.POWER_TIME_UNLIMITED
+ elif energy_now is not None and power_now is not None:
+ try:
+ secsleft = int(energy_now / power_now * 3600)
+ except ZeroDivisionError:
+ secsleft = _common.POWER_TIME_UNKNOWN
+ elif time_to_empty is not None:
+ secsleft = int(time_to_empty * 60)
+ if secsleft < 0:
+ secsleft = _common.POWER_TIME_UNKNOWN
+ else:
+ secsleft = _common.POWER_TIME_UNKNOWN
+
+ return _common.sbattery(percent, secsleft, power_plugged)
+
+
+# =====================================================================
+# --- other system functions
+# =====================================================================
+
+
+def users():
+ """Return currently connected users as a list of namedtuples."""
+ 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'
+ nt = _common.suser(user, tty or None, hostname, tstamp, pid)
+ retlist.append(nt)
+ return retlist
+
+
+def boot_time():
+ """Return the system boot time expressed in seconds since the epoch."""
+ global BOOT_TIME
+ path = '%s/stat' % get_procfs_path()
+ with open_binary(path) as f:
+ for line in f:
+ if line.startswith(b'btime'):
+ ret = float(line.strip().split()[1])
+ BOOT_TIME = ret
+ return ret
+ raise RuntimeError(
+ "line 'btime' not found in %s" % path)
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+def pids():
+ """Returns a list of PIDs currently running on the system."""
+ return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()]
+
+
+def pid_exists(pid):
+ """Check for the existence of a unix PID. Linux TIDs are not
+ supported (always return False).
+ """
+ if not _psposix.pid_exists(pid):
+ return False
+ else:
+ # Linux's apparently does not distinguish between PIDs and TIDs
+ # (thread IDs).
+ # listdir("/proc") won't show any TID (only PIDs) but
+ # os.stat("/proc/{tid}") will succeed if {tid} exists.
+ # os.kill() can also be passed a TID. This is quite confusing.
+ # In here we want to enforce this distinction and support PIDs
+ # only, see:
+ # https://github.com/giampaolo/psutil/issues/687
+ try:
+ # Note: already checked that this is faster than using a
+ # regular expr. Also (a lot) faster than doing
+ # 'return pid in pids()'
+ path = "%s/%s/status" % (get_procfs_path(), pid)
+ with open_binary(path) as f:
+ for line in f:
+ if line.startswith(b"Tgid:"):
+ tgid = int(line.split()[1])
+ # If tgid and pid are the same then we're
+ # dealing with a process PID.
+ return tgid == pid
+ raise ValueError("'Tgid' line not found in %s" % path)
+ except (EnvironmentError, ValueError):
+ return pid in pids()
+
+
+def ppid_map():
+ """Obtain a {pid: ppid, ...} dict for all running processes in
+ one shot. Used to speed up Process.children().
+ """
+ ret = {}
+ procfs_path = get_procfs_path()
+ for pid in pids():
+ try:
+ with open_binary("%s/%s/stat" % (procfs_path, pid)) as f:
+ data = f.read()
+ except (FileNotFoundError, ProcessLookupError):
+ # Note: we should be able to access /stat for all processes
+ # aka it's unlikely we'll bump into EPERM, which is good.
+ pass
+ else:
+ rpar = data.rfind(b')')
+ dset = data[rpar + 2:].split()
+ ppid = int(dset[1])
+ ret[pid] = ppid
+ return ret
+
+
+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:
+ return fun(self, *args, **kwargs)
+ except PermissionError:
+ raise AccessDenied(self.pid, self._name)
+ except ProcessLookupError:
+ raise NoSuchProcess(self.pid, self._name)
+ except FileNotFoundError:
+ 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):
+ """Linux process implementation."""
+
+ __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"]
+
+ def __init__(self, pid):
+ self.pid = pid
+ self._name = None
+ self._ppid = None
+ self._procfs_path = get_procfs_path()
+
+ def _assert_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.
+ os.stat('%s/%s' % (self._procfs_path, self.pid))
+
+ @wrap_exceptions
+ @memoize_when_activated
+ def _parse_stat_file(self):
+ """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
+ '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()
+ # 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 ")".
+ rpar = data.rfind(b')')
+ name = data[data.find(b'(') + 1:rpar]
+ fields = data[rpar + 2:].split()
+
+ ret = {}
+ ret['name'] = name
+ ret['status'] = fields[0]
+ ret['ppid'] = fields[1]
+ ret['ttynr'] = fields[4]
+ ret['utime'] = fields[11]
+ ret['stime'] = fields[12]
+ ret['children_utime'] = fields[13]
+ ret['children_stime'] = fields[14]
+ ret['create_time'] = fields[19]
+ ret['cpu_num'] = fields[36]
+ ret['blkio_ticks'] = fields[39] # aka 'delayacct_blkio_ticks'
+
+ return ret
+
+ @wrap_exceptions
+ @memoize_when_activated
+ def _read_status_file(self):
+ """Read /proc/{pid}/stat file and return its content.
+ The return value is cached in case oneshot() ctx manager is
+ in use.
+ """
+ with open_binary("%s/%s/status" % (self._procfs_path, self.pid)) as f:
+ return f.read()
+
+ @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:
+ return f.read().strip()
+
+ def oneshot_enter(self):
+ self._parse_stat_file.cache_activate(self)
+ self._read_status_file.cache_activate(self)
+ self._read_smaps_file.cache_activate(self)
+
+ def oneshot_exit(self):
+ self._parse_stat_file.cache_deactivate(self)
+ self._read_status_file.cache_deactivate(self)
+ self._read_smaps_file.cache_deactivate(self)
+
+ @wrap_exceptions
+ def name(self):
+ name = self._parse_stat_file()['name']
+ if PY3:
+ name = decode(name)
+ # XXX - gets changed later and probably needs refactoring
+ return name
+
+ def exe(self):
+ try:
+ return readlink("%s/%s/exe" % (self._procfs_path, self.pid))
+ except (FileNotFoundError, ProcessLookupError):
+ # 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)
+
+ @wrap_exceptions
+ def cmdline(self):
+ with open_text("%s/%s/cmdline" % (self._procfs_path, self.pid)) as f:
+ data = f.read()
+ if not data:
+ # may happen in case of zombie process
+ return []
+ # 'man proc' states that args are separated by null bytes '\0'
+ # and last char is supposed to be a null byte. Nevertheless
+ # some processes may change their cmdline after being started
+ # (via setproctitle() or similar), they are usually not
+ # compliant with this rule and use spaces instead. Google
+ # Chrome process is an example. See:
+ # https://github.com/giampaolo/psutil/issues/1179
+ sep = '\x00' if data.endswith('\x00') else ' '
+ if data.endswith(sep):
+ data = data[:-1]
+ cmdline = data.split(sep)
+ # Sometimes last char is a null byte '\0' but the args are
+ # separated by spaces, see: https://github.com/giampaolo/psutil/
+ # issues/1179#issuecomment-552984549
+ if sep == '\x00' and len(cmdline) == 1 and ' ' in data:
+ cmdline = data.split(' ')
+ return cmdline
+
+ @wrap_exceptions
+ def environ(self):
+ with open_text("%s/%s/environ" % (self._procfs_path, self.pid)) as f:
+ data = f.read()
+ return parse_environ_block(data)
+
+ @wrap_exceptions
+ def terminal(self):
+ tty_nr = int(self._parse_stat_file()['ttynr'])
+ tmap = _psposix.get_terminal_map()
+ try:
+ return tmap[tty_nr]
+ except KeyError:
+ return None
+
+ # 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)
+ fields = {}
+ with open_binary(fname) as f:
+ for line in f:
+ # https://github.com/giampaolo/psutil/issues/1004
+ line = line.strip()
+ if line:
+ try:
+ name, value = line.split(b': ')
+ except ValueError:
+ # https://github.com/giampaolo/psutil/issues/1004
+ continue
+ else:
+ fields[name] = int(value)
+ if not fields:
+ raise RuntimeError("%s file was empty" % fname)
+ try:
+ return pio(
+ fields[b'syscr'], # read syscalls
+ fields[b'syscw'], # write syscalls
+ fields[b'read_bytes'], # read bytes
+ fields[b'write_bytes'], # write bytes
+ fields[b'rchar'], # read chars
+ 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))
+
+ @wrap_exceptions
+ def cpu_times(self):
+ values = self._parse_stat_file()
+ utime = float(values['utime']) / CLOCK_TICKS
+ stime = float(values['stime']) / CLOCK_TICKS
+ children_utime = float(values['children_utime']) / CLOCK_TICKS
+ children_stime = float(values['children_stime']) / CLOCK_TICKS
+ iowait = float(values['blkio_ticks']) / CLOCK_TICKS
+ return pcputimes(utime, stime, children_utime, children_stime, iowait)
+
+ @wrap_exceptions
+ def cpu_num(self):
+ """What CPU the process is on."""
+ return int(self._parse_stat_file()['cpu_num'])
+
+ @wrap_exceptions
+ def wait(self, timeout=None):
+ return _psposix.wait_pid(self.pid, timeout, self._name)
+
+ @wrap_exceptions
+ def create_time(self):
+ ctime = float(self._parse_stat_file()['create_time'])
+ # According to documentation, starttime is in field 21 and the
+ # unit is jiffies (clock ticks).
+ # We first divide it for clock ticks and then add uptime returning
+ # seconds since the epoch.
+ # Also use cached value if available.
+ bt = BOOT_TIME or boot_time()
+ return (ctime / CLOCK_TICKS) + bt
+
+ @wrap_exceptions
+ def memory_info(self):
+ # ============================================================
+ # | FIELD | DESCRIPTION | AKA | TOP |
+ # ============================================================
+ # | rss | resident set size | | RES |
+ # | vms | total program size | size | VIRT |
+ # | shared | shared pages (from shared mappings) | | SHR |
+ # | text | text ('code') | trs | CODE |
+ # | lib | library (unused in Linux 2.6) | lrs | |
+ # | data | data + stack | drs | DATA |
+ # | 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]]
+ 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:
+
+ @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()
+ # 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
+ # Python 2 though. Figure out why.
+ #
+ # You might be tempted to calculate USS by subtracting
+ # the "shared" value from the "resident" value in
+ # /proc/<pid>/statm. But at least on Linux, statm's "shared"
+ # value actually counts pages backed by files, which has
+ # little to do with whether the pages are actually shared.
+ # /proc/self/smaps on the other hand appears to give us the
+ # correct information.
+ smaps_data = self._read_smaps_file()
+ # Note: smaps file can be empty for certain processes.
+ # The code below will not crash though and will result to 0.
+ 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 pfullmem(*basic_mem + (uss, pss, swap))
+
+ else:
+ memory_full_info = memory_info
+
+ if HAS_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
+
+ /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:
+ fields = line.split(None, 5)
+ if not fields[0].endswith(b':'):
+ # new block section
+ yield (current_block.pop(), data)
+ current_block.append(line)
+ else:
+ try:
+ data[fields[0]] = int(fields[1]) * 1024
+ except ValueError:
+ if fields[0].startswith(b'VmFlags:'):
+ # see issue #369
+ continue
+ else:
+ raise ValueError("don't know how to inte"
+ "rpret line %r" % line)
+ yield (current_block.pop(), data)
+
+ data = self._read_smaps_file()
+ # Note: smaps file can be empty for certain processes.
+ if not data:
+ return []
+ lines = data.split(b'\n')
+ ls = []
+ first_line = lines.pop(0)
+ current_block = [first_line]
+ for header, data in get_blocks(lines, current_block):
+ hfields = header.split(None, 5)
+ try:
+ addr, perms, offset, dev, inode, path = hfields
+ except ValueError:
+ 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)):
+ path = path[:-10]
+ ls.append((
+ decode(addr), decode(perms), path,
+ data.get(b'Rss:', 0),
+ data.get(b'Size:', 0),
+ data.get(b'Pss:', 0),
+ data.get(b'Shared_Clean:', 0),
+ data.get(b'Shared_Dirty:', 0),
+ data.get(b'Private_Clean:', 0),
+ data.get(b'Private_Dirty:', 0),
+ data.get(b'Referenced:', 0),
+ data.get(b'Anonymous:', 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)
+
+ @wrap_exceptions
+ 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))
+ else:
+ return _common.pctxsw(int(ctxsw[0]), int(ctxsw[1]))
+
+ @wrap_exceptions
+ def num_threads(self, _num_threads_re=re.compile(br'Threads:\t(\d+)')):
+ # Note: on Python 3 using a re is faster than iterating over file
+ # line by line. On Python 2 is the exact opposite, and iterating
+ # over a file on Python 3 is slower than on Python 2.
+ data = self._read_status_file()
+ return int(_num_threads_re.findall(data)[0])
+
+ @wrap_exceptions
+ def threads(self):
+ thread_ids = os.listdir("%s/%s/task" % (self._procfs_path, self.pid))
+ thread_ids.sort()
+ retlist = []
+ hit_enoent = False
+ for thread_id in thread_ids:
+ fname = "%s/%s/task/%s/stat" % (
+ 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
+ hit_enoent = True
+ continue
+ # ignore the first two values ("pid (exe)")
+ 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()
+ return retlist
+
+ @wrap_exceptions
+ def nice_get(self):
+ # with open_text('%s/%s/stat' % (self._procfs_path, self.pid)) as f:
+ # data = f.read()
+ # return int(data.split()[18])
+
+ # Use C implementation
+ return cext_posix.getpriority(self.pid)
+
+ @wrap_exceptions
+ def nice_set(self, value):
+ return cext_posix.setpriority(self.pid, value)
+
+ # starting from CentOS 6.
+ if HAS_CPU_AFFINITY:
+
+ @wrap_exceptions
+ def cpu_affinity_get(self):
+ return cext.proc_cpu_affinity_get(self.pid)
+
+ def _get_eligible_cpus(
+ 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)
+ if match:
+ return list(range(int(match[0][0]), int(match[0][1]) + 1))
+ else:
+ return list(range(len(per_cpu_times())))
+
+ @wrap_exceptions
+ def cpu_affinity_set(self, cpus):
+ try:
+ cext.proc_cpu_affinity_set(self.pid, cpus)
+ except (OSError, ValueError) as err:
+ if isinstance(err, ValueError) or err.errno == errno.EINVAL:
+ eligible_cpus = self._get_eligible_cpus()
+ all_cpus = tuple(range(len(per_cpu_times())))
+ for cpu in cpus:
+ if cpu not in all_cpus:
+ raise ValueError(
+ "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))
+ raise
+
+ # only starting from kernel 2.6.13
+ if HAS_PROC_IO_PRIORITY:
+
+ @wrap_exceptions
+ def ionice_get(self):
+ ioclass, value = cext.proc_ioprio_get(self.pid)
+ if enum is not None:
+ ioclass = IOPriority(ioclass)
+ return _common.pionice(ioclass, value)
+
+ @wrap_exceptions
+ def ionice_set(self, ioclass, value):
+ if value is None:
+ value = 0
+ 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")
+ return cext.proc_ioprio_set(self.pid, ioclass, value)
+
+ if prlimit is not None:
+
+ @wrap_exceptions
+ def rlimit(self, resource_, limits=None):
+ # If pid is 0 prlimit() applies to the calling process and
+ # 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")
+ try:
+ if limits is None:
+ # get
+ return prlimit(self.pid, resource_)
+ else:
+ # set
+ if len(limits) != 2:
+ raise ValueError(
+ "second argument must be a (soft, hard) tuple, "
+ "got %s" % repr(limits))
+ prlimit(self.pid, resource_, limits)
+ except OSError as err:
+ if err.errno == errno.ENOSYS and pid_exists(self.pid):
+ # I saw this happening on Travis:
+ # https://travis-ci.org/giampaolo/psutil/jobs/51368273
+ raise ZombieProcess(self.pid, self._name, self._ppid)
+ else:
+ raise
+
+ @wrap_exceptions
+ def status(self):
+ letter = self._parse_stat_file()['status']
+ if PY3:
+ letter = letter.decode()
+ # XXX is '?' legit? (we're not supposed to return it anyway)
+ return PROC_STATUSES.get(letter, '?')
+
+ @wrap_exceptions
+ def open_files(self):
+ retlist = []
+ files = os.listdir("%s/%s/fd" % (self._procfs_path, self.pid))
+ hit_enoent = False
+ for fd in files:
+ file = "%s/%s/fd/%s" % (self._procfs_path, self.pid, fd)
+ try:
+ path = readlink(file)
+ except (FileNotFoundError, ProcessLookupError):
+ # ENOENT == file which is gone in the meantime
+ hit_enoent = True
+ continue
+ except OSError as err:
+ if err.errno == errno.EINVAL:
+ # not a link
+ continue
+ raise
+ else:
+ # If path is not an absolute there's no way to tell
+ # whether it's a regular file or not, so we skip it.
+ # A regular file is always supposed to be have an
+ # absolute path though.
+ if path.startswith('/') and isfile_strict(path):
+ # Get file position and flags.
+ file = "%s/%s/fdinfo/%s" % (
+ 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:
+ # 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)
+ retlist.append(ntuple)
+ if hit_enoent:
+ self._assert_alive()
+ return retlist
+
+ @wrap_exceptions
+ def connections(self, kind='inet'):
+ ret = _connections.retrieve(kind, self.pid)
+ self._assert_alive()
+ return ret
+
+ @wrap_exceptions
+ def num_fds(self):
+ return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)))
+
+ @wrap_exceptions
+ def ppid(self):
+ return int(self._parse_stat_file()['ppid'])
+
+ @wrap_exceptions
+ def uids(self, _uids_re=re.compile(br'Uid:\t(\d+)\t(\d+)\t(\d+)')):
+ data = self._read_status_file()
+ real, effective, saved = _uids_re.findall(data)[0]
+ return _common.puids(int(real), int(effective), int(saved))
+
+ @wrap_exceptions
+ def gids(self, _gids_re=re.compile(br'Gid:\t(\d+)\t(\d+)\t(\d+)')):
+ data = self._read_status_file()
+ real, effective, saved = _gids_re.findall(data)[0]
+ return _common.pgids(int(real), int(effective), int(saved))
diff --git a/contrib/python/psutil/py3/psutil/_psosx.py b/contrib/python/psutil/py3/psutil/_psosx.py
new file mode 100644
index 0000000000..c7770d6591
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_psosx.py
@@ -0,0 +1,577 @@
+# 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.
+
+"""macOS platform implementation."""
+
+import contextlib
+import errno
+import functools
+import os
+from collections import namedtuple
+
+from . import _common
+from . import _psposix
+from . import _psutil_osx as cext
+from . import _psutil_posix as cext_posix
+from ._common import AccessDenied
+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
+
+
+__extra__all__ = []
+
+
+# =====================================================================
+# --- globals
+# =====================================================================
+
+
+PAGESIZE = cext_posix.getpagesize()
+AF_LINK = cext_posix.AF_LINK
+
+TCP_STATUSES = {
+ cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
+ cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
+ cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV,
+ cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
+ cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
+ cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
+ cext.TCPS_CLOSED: _common.CONN_CLOSE,
+ cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
+ cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
+ cext.TCPS_LISTEN: _common.CONN_LISTEN,
+ cext.TCPS_CLOSING: _common.CONN_CLOSING,
+ cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
+}
+
+PROC_STATUSES = {
+ cext.SIDL: _common.STATUS_IDLE,
+ cext.SRUN: _common.STATUS_RUNNING,
+ cext.SSLEEP: _common.STATUS_SLEEPING,
+ cext.SSTOP: _common.STATUS_STOPPED,
+ cext.SZOMB: _common.STATUS_ZOMBIE,
+}
+
+kinfo_proc_map = dict(
+ ppid=0,
+ ruid=1,
+ euid=2,
+ suid=3,
+ rgid=4,
+ egid=5,
+ sgid=6,
+ ttynr=7,
+ ctime=8,
+ status=9,
+ name=10,
+)
+
+pidtaskinfo_map = dict(
+ cpuutime=0,
+ cpustime=1,
+ rss=2,
+ vms=3,
+ pfaults=4,
+ pageins=5,
+ numthreads=6,
+ volctxsw=7,
+)
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.cpu_times()
+scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle'])
+# psutil.virtual_memory()
+svmem = namedtuple(
+ 'svmem', ['total', 'available', 'percent', 'used', 'free',
+ 'active', 'inactive', 'wired'])
+# psutil.Process.memory_info()
+pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins'])
+# psutil.Process.memory_full_info()
+pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
+
+
+# =====================================================================
+# --- memory
+# =====================================================================
+
+
+def virtual_memory():
+ """System virtual memory as a namedtuple."""
+ total, active, inactive, wired, free, speculative = cext.virtual_mem()
+ # This is how Zabbix calculate avail and used mem:
+ # https://github.com/zabbix/zabbix/blob/trunk/src/libs/zbxsysinfo/
+ # osx/memory.c
+ # Also see: https://github.com/giampaolo/psutil/issues/1277
+ avail = inactive + free
+ used = active + wired
+ # This is NOT how Zabbix calculates free mem but it matches "free"
+ # cmdline utility.
+ free -= speculative
+ percent = usage_percent((total - avail), total, round_=1)
+ return svmem(total, avail, percent, used, free,
+ active, inactive, wired)
+
+
+def swap_memory():
+ """Swap system memory as a (total, used, free, sin, sout) tuple."""
+ total, used, free, sin, sout = cext.swap_mem()
+ percent = usage_percent(used, total, round_=1)
+ return _common.sswap(total, used, free, percent, sin, sout)
+
+
+# =====================================================================
+# --- CPU
+# =====================================================================
+
+
+def cpu_times():
+ """Return system CPU times as a namedtuple."""
+ user, nice, system, idle = cext.cpu_times()
+ return scputimes(user, nice, system, idle)
+
+
+def per_cpu_times():
+ """Return system CPU times as a named tuple"""
+ ret = []
+ for cpu_t in cext.per_cpu_times():
+ user, nice, system, idle = cpu_t
+ item = scputimes(user, nice, system, idle)
+ ret.append(item)
+ return ret
+
+
+def cpu_count_logical():
+ """Return the number of logical CPUs in the system."""
+ 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_stats():
+ ctx_switches, interrupts, soft_interrupts, syscalls, traps = \
+ cext.cpu_stats()
+ return _common.scpustats(
+ 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
+ """
+ curr, min_, max_ = cext.cpu_freq()
+ return [_common.scpufreq(curr, min_, max_)]
+
+
+# =====================================================================
+# --- disks
+# =====================================================================
+
+
+disk_usage = _psposix.disk_usage
+disk_io_counters = cext.disk_io_counters
+
+
+def disk_partitions(all=False):
+ """Return mounted disk partitions as a list of namedtuples."""
+ retlist = []
+ partitions = cext.disk_partitions()
+ for partition in partitions:
+ device, mountpoint, fstype, opts = partition
+ if device == 'none':
+ device = ''
+ if not all:
+ 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)
+ retlist.append(ntuple)
+ return retlist
+
+
+# =====================================================================
+# --- sensors
+# =====================================================================
+
+
+def sensors_battery():
+ """Return battery information."""
+ try:
+ percent, minsleft, power_plugged = cext.sensors_battery()
+ except NotImplementedError:
+ # no power source - return None according to interface
+ return None
+ power_plugged = power_plugged == 1
+ if power_plugged:
+ secsleft = _common.POWER_TIME_UNLIMITED
+ elif minsleft == -1:
+ secsleft = _common.POWER_TIME_UNKNOWN
+ else:
+ secsleft = minsleft * 60
+ return _common.sbattery(percent, secsleft, power_plugged)
+
+
+# =====================================================================
+# --- network
+# =====================================================================
+
+
+net_io_counters = cext.net_io_counters
+net_if_addrs = cext_posix.net_if_addrs
+
+
+def net_connections(kind='inet'):
+ """System-wide network connections."""
+ # Note: on macOS this will fail with AccessDenied unless
+ # the process is owned by root.
+ ret = []
+ for pid in pids():
+ try:
+ cons = Process(pid).connections(kind)
+ except NoSuchProcess:
+ continue
+ else:
+ if cons:
+ for c in cons:
+ c = list(c) + [pid]
+ ret.append(_common.sconn(*c))
+ return ret
+
+
+def net_if_stats():
+ """Get NIC stats (isup, duplex, speed, mtu)."""
+ 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)
+ duplex, speed = cext_posix.net_if_duplex_speed(name)
+ except OSError as err:
+ # https://github.com/giampaolo/psutil/issues/1279
+ if err.errno != errno.ENODEV:
+ raise
+ else:
+ if hasattr(_common, 'NicDuplex'):
+ duplex = _common.NicDuplex(duplex)
+ ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ return ret
+
+
+# =====================================================================
+# --- other system functions
+# =====================================================================
+
+
+def boot_time():
+ """The system boot time expressed in seconds since the epoch."""
+ return cext.boot_time()
+
+
+def users():
+ """Return currently connected users as a list of namedtuples."""
+ retlist = []
+ rawlist = cext.users()
+ for item in rawlist:
+ user, tty, hostname, tstamp, pid = item
+ if tty == '~':
+ continue # reboot or shutdown
+ if not tstamp:
+ continue
+ nt = _common.suser(user, tty or None, hostname or None, tstamp, pid)
+ retlist.append(nt)
+ return retlist
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+def pids():
+ ls = cext.pids()
+ if 0 not in ls:
+ # On certain macOS versions pids() C doesn't return PID 0 but
+ # "ps" does and the process is querable via sysctl():
+ # https://travis-ci.org/giampaolo/psutil/jobs/309619941
+ try:
+ Process(0).create_time()
+ ls.insert(0, 0)
+ except NoSuchProcess:
+ pass
+ except AccessDenied:
+ ls.insert(0, 0)
+ return ls
+
+
+pid_exists = _psposix.pid_exists
+
+
+def is_zombie(pid):
+ try:
+ st = cext.proc_kinfo_oneshot(pid)[kinfo_proc_map['status']]
+ return st == cext.SZOMB
+ except Exception:
+ return False
+
+
+def wrap_exceptions(fun):
+ """Decorator which translates bare OSError exceptions into
+ NoSuchProcess and AccessDenied.
+ """
+ @functools.wraps(fun)
+ def wrapper(self, *args, **kwargs):
+ try:
+ return fun(self, *args, **kwargs)
+ except ProcessLookupError:
+ if is_zombie(self.pid):
+ raise ZombieProcess(self.pid, self._name, self._ppid)
+ else:
+ 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
+
+
+class Process(object):
+ """Wrapper class around underlying C implementation."""
+
+ __slots__ = ["pid", "_name", "_ppid", "_cache"]
+
+ def __init__(self, pid):
+ self.pid = pid
+ self._name = None
+ self._ppid = None
+
+ @wrap_exceptions
+ @memoize_when_activated
+ def _get_kinfo_proc(self):
+ # Note: should work with all PIDs without permission issues.
+ ret = cext.proc_kinfo_oneshot(self.pid)
+ assert len(ret) == len(kinfo_proc_map)
+ return ret
+
+ @wrap_exceptions
+ @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)
+ assert len(ret) == len(pidtaskinfo_map)
+ return ret
+
+ def oneshot_enter(self):
+ self._get_kinfo_proc.cache_activate(self)
+ self._get_pidtaskinfo.cache_activate(self)
+
+ def oneshot_exit(self):
+ self._get_kinfo_proc.cache_deactivate(self)
+ self._get_pidtaskinfo.cache_deactivate(self)
+
+ @wrap_exceptions
+ def name(self):
+ name = self._get_kinfo_proc()[kinfo_proc_map['name']]
+ return name if name is not None else cext.proc_name(self.pid)
+
+ @wrap_exceptions
+ def exe(self):
+ with catch_zombie(self):
+ return cext.proc_exe(self.pid)
+
+ @wrap_exceptions
+ def cmdline(self):
+ with catch_zombie(self):
+ return cext.proc_cmdline(self.pid)
+
+ @wrap_exceptions
+ def environ(self):
+ with catch_zombie(self):
+ return parse_environ_block(cext.proc_environ(self.pid))
+
+ @wrap_exceptions
+ def ppid(self):
+ self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']]
+ return self._ppid
+
+ @wrap_exceptions
+ def cwd(self):
+ with catch_zombie(self):
+ return cext.proc_cwd(self.pid)
+
+ @wrap_exceptions
+ def uids(self):
+ rawtuple = self._get_kinfo_proc()
+ return _common.puids(
+ rawtuple[kinfo_proc_map['ruid']],
+ rawtuple[kinfo_proc_map['euid']],
+ rawtuple[kinfo_proc_map['suid']])
+
+ @wrap_exceptions
+ def gids(self):
+ rawtuple = self._get_kinfo_proc()
+ return _common.puids(
+ rawtuple[kinfo_proc_map['rgid']],
+ rawtuple[kinfo_proc_map['egid']],
+ rawtuple[kinfo_proc_map['sgid']])
+
+ @wrap_exceptions
+ def terminal(self):
+ tty_nr = self._get_kinfo_proc()[kinfo_proc_map['ttynr']]
+ tmap = _psposix.get_terminal_map()
+ try:
+ return tmap[tty_nr]
+ except KeyError:
+ return None
+
+ @wrap_exceptions
+ def memory_info(self):
+ rawtuple = self._get_pidtaskinfo()
+ return pmem(
+ rawtuple[pidtaskinfo_map['rss']],
+ rawtuple[pidtaskinfo_map['vms']],
+ rawtuple[pidtaskinfo_map['pfaults']],
+ rawtuple[pidtaskinfo_map['pageins']],
+ )
+
+ @wrap_exceptions
+ def memory_full_info(self):
+ basic_mem = self.memory_info()
+ uss = cext.proc_memory_uss(self.pid)
+ return pfullmem(*basic_mem + (uss, ))
+
+ @wrap_exceptions
+ def cpu_times(self):
+ rawtuple = self._get_pidtaskinfo()
+ return _common.pcputimes(
+ rawtuple[pidtaskinfo_map['cpuutime']],
+ rawtuple[pidtaskinfo_map['cpustime']],
+ # children user / system times are not retrievable (set to 0)
+ 0.0, 0.0)
+
+ @wrap_exceptions
+ def create_time(self):
+ return self._get_kinfo_proc()[kinfo_proc_map['ctime']]
+
+ @wrap_exceptions
+ def num_ctx_switches(self):
+ # Unvoluntary value seems not to be available;
+ # getrusage() numbers seems to confirm this theory.
+ # We set it to 0.
+ vol = self._get_pidtaskinfo()[pidtaskinfo_map['volctxsw']]
+ return _common.pctxsw(vol, 0)
+
+ @wrap_exceptions
+ def num_threads(self):
+ return self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']]
+
+ @wrap_exceptions
+ def open_files(self):
+ if self.pid == 0:
+ return []
+ files = []
+ with catch_zombie(self):
+ rawlist = cext.proc_open_files(self.pid)
+ for path, fd in rawlist:
+ if isfile_strict(path):
+ ntuple = _common.popenfile(path, fd)
+ files.append(ntuple)
+ return files
+
+ @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])))
+ families, types = conn_tmap[kind]
+ with catch_zombie(self):
+ 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)
+ ret.append(nt)
+ return ret
+
+ @wrap_exceptions
+ def num_fds(self):
+ if self.pid == 0:
+ return 0
+ with catch_zombie(self):
+ return cext.proc_num_fds(self.pid)
+
+ @wrap_exceptions
+ def wait(self, timeout=None):
+ return _psposix.wait_pid(self.pid, timeout, self._name)
+
+ @wrap_exceptions
+ def nice_get(self):
+ with catch_zombie(self):
+ return cext_posix.getpriority(self.pid)
+
+ @wrap_exceptions
+ def nice_set(self, value):
+ with catch_zombie(self):
+ return cext_posix.setpriority(self.pid, value)
+
+ @wrap_exceptions
+ def status(self):
+ code = self._get_kinfo_proc()[kinfo_proc_map['status']]
+ # XXX is '?' legit? (we're not supposed to return it anyway)
+ return PROC_STATUSES.get(code, '?')
+
+ @wrap_exceptions
+ def threads(self):
+ rawlist = cext.proc_threads(self.pid)
+ retlist = []
+ for thread_id, utime, stime in rawlist:
+ ntuple = _common.pthread(thread_id, utime, stime)
+ retlist.append(ntuple)
+ return retlist
diff --git a/contrib/python/psutil/py3/psutil/_psposix.py b/contrib/python/psutil/py3/psutil/_psposix.py
new file mode 100644
index 0000000000..706dab9ae8
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_psposix.py
@@ -0,0 +1,223 @@
+# 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.
+
+"""Routines common to all posix systems."""
+
+import glob
+import os
+import signal
+import sys
+import time
+
+from ._common import memoize
+from ._common import sdiskusage
+from ._common import TimeoutExpired
+from ._common import usage_percent
+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):
+ import enum
+else:
+ enum = None
+
+
+__all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map']
+
+
+def pid_exists(pid):
+ """Check whether pid exists in the current process table."""
+ if pid == 0:
+ # According to "man 2 kill" PID 0 has a special meaning:
+ # it refers to <<every process in the process group of the
+ # calling process>> so we don't want to go any further.
+ # If we get here it means this UNIX platform *does* have
+ # a process with id 0.
+ return True
+ try:
+ os.kill(pid, 0)
+ except ProcessLookupError:
+ return False
+ except PermissionError:
+ # EPERM clearly means there's a process to deny access to
+ return True
+ # According to "man 2 kill" possible error values are
+ # (EINVAL, EPERM, ESRCH)
+ else:
+ return True
+
+
+# Python 3.5 signals enum (contributed by me ^^):
+# 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]))
+
+ def negsig_to_enum(num):
+ """Convert a negative signal value to an enum."""
+ try:
+ 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):
+ """Wait for a process PID to terminate.
+
+ If the 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().
+
+ If it was terminated by a signal it returns the negated value of the
+ signal which caused the termination (e.g. -SIGTERM).
+
+ If PID is not a children of os.getpid() (current process) just
+ wait until the process disappears and return None.
+
+ If PID does not exist at all return None immediately.
+
+ If *timeout* != None and process is still alive raise TimeoutExpired.
+ timeout=0 is also possible (either return immediately or raise).
+ """
+ if pid <= 0:
+ raise ValueError("can't wait for PID 0") # see "man waitpid"
+ interval = 0.0001
+ flags = 0
+ if timeout is not None:
+ flags |= os.WNOHANG
+ stop_at = _timer() + timeout
+
+ def sleep(interval):
+ # Sleep for some time and return a new increased interval.
+ if timeout is not None:
+ if _timer() >= stop_at:
+ raise TimeoutExpired(timeout, pid=pid, name=proc_name)
+ _sleep(interval)
+ return _min(interval * 2, 0.04)
+
+ # See: https://linux.die.net/man/2/waitpid
+ while True:
+ try:
+ retpid, status = os.waitpid(pid, flags)
+ except InterruptedError:
+ interval = sleep(interval)
+ except ChildProcessError:
+ # This has two meanings:
+ # - PID is not a child of os.getpid() in which case
+ # we keep polling until it's gone
+ # - PID never existed in the first place
+ # In both cases we'll eventually return None as we
+ # can't determine its exit status code.
+ while _pid_exists(pid):
+ interval = sleep(interval)
+ return
+ else:
+ if retpid == 0:
+ # WNOHANG flag was used and PID is still running.
+ interval = sleep(interval)
+ continue
+ elif 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().
+ return os.WEXITSTATUS(status)
+ elif os.WIFSIGNALED(status):
+ # Process exited due to a signal. Return the negative value
+ # of that signal.
+ return negsig_to_enum(-os.WTERMSIG(status))
+ # elif os.WIFSTOPPED(status):
+ # # Process was stopped via SIGSTOP or is being traced, and
+ # # waitpid() was called with WUNTRACED flag. PID is still
+ # # alive. From now on waitpid() will keep returning (0, 0)
+ # # until the process state doesn't change.
+ # # It may make sense to catch/enable this since stopped PIDs
+ # # ignore SIGTERM.
+ # interval = sleep(interval)
+ # continue
+ # elif os.WIFCONTINUED(status):
+ # # Process was resumed via SIGCONT and waitpid() was called
+ # # with WCONTINUED flag.
+ # interval = sleep(interval)
+ # continue
+ else:
+ # Should never happen.
+ raise ValueError("unknown process exit status %r" % status)
+
+
+def disk_usage(path):
+ """Return disk usage associated with path.
+ Note: UNIX usually reserves 5% disk space which is not accessible
+ by user. In this function "total" and "used" values reflect the
+ total and used disk space whereas "free" and "percent" represent
+ the "free" and "used percent" user disk space.
+ """
+ if PY3:
+ st = os.statvfs(path)
+ else: # pragma: no cover
+ # os.statvfs() does not support unicode on Python 2:
+ # - https://github.com/giampaolo/psutil/issues/416
+ # - http://bugs.python.org/issue18695
+ try:
+ st = os.statvfs(path)
+ except UnicodeEncodeError:
+ if isinstance(path, unicode):
+ try:
+ path = path.encode(sys.getfilesystemencoding())
+ except UnicodeEncodeError:
+ pass
+ st = os.statvfs(path)
+ else:
+ raise
+
+ # Total space which is only available to root (unless changed
+ # at system level).
+ total = (st.f_blocks * st.f_frsize)
+ # Remaining free space usable by root.
+ avail_to_root = (st.f_bfree * st.f_frsize)
+ # Remaining free space usable by user.
+ avail_to_user = (st.f_bavail * st.f_frsize)
+ # Total space being used in general.
+ used = (total - avail_to_root)
+ # Total space which is available to user (same as 'total' but
+ # for the user).
+ total_user = used + avail_to_user
+ # User usage percent compared to the total amount of space
+ # the user can use. This number would be higher if compared
+ # to root's because the user has less space (usually -5%).
+ usage_percent_user = usage_percent(used, total_user, round_=1)
+
+ # NB: the percentage is -5% than what shown by df due to
+ # 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)
+
+
+@memoize
+def get_terminal_map():
+ """Get a map of device-id -> path as a dict.
+ Used by Process.terminal()
+ """
+ ret = {}
+ ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*')
+ for name in ls:
+ assert name not in ret, name
+ try:
+ ret[os.stat(name).st_rdev] = name
+ except FileNotFoundError:
+ pass
+ return ret
diff --git a/contrib/python/psutil/py3/psutil/_psutil_common.c b/contrib/python/psutil/py3/psutil/_psutil_common.c
new file mode 100644
index 0000000000..f371167f3e
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_psutil_common.c
@@ -0,0 +1,429 @@
+/*
+ * 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.
+ *
+ * Routines common to all platforms.
+ */
+
+#include <Python.h>
+#define PSUTIL_MAYBE_EXTERN
+#include "_psutil_common.h"
+
+// ====================================================================
+// --- Global vars
+// ====================================================================
+
+int PSUTIL_DEBUG = 0;
+int PSUTIL_TESTING = 0;
+// PSUTIL_CONN_NONE
+
+
+// ====================================================================
+// --- Backward compatibility with missing Python.h APIs
+// ====================================================================
+
+// PyPy on Windows
+#if defined(PSUTIL_WINDOWS) && defined(PYPY_VERSION)
+#if !defined(PyErr_SetFromWindowsErrWithFilename)
+PyObject *
+PyErr_SetFromWindowsErrWithFilename(int winerr, const char *filename) {
+ PyObject *py_exc = NULL;
+ PyObject *py_winerr = NULL;
+
+ if (winerr == 0)
+ winerr = GetLastError();
+ if (filename == NULL) {
+ py_exc = PyObject_CallFunction(PyExc_OSError, "(is)", winerr,
+ strerror(winerr));
+ }
+ else {
+ py_exc = PyObject_CallFunction(PyExc_OSError, "(iss)", winerr,
+ strerror(winerr), filename);
+ }
+ if (py_exc == NULL)
+ return NULL;
+
+ py_winerr = Py_BuildValue("i", winerr);
+ if (py_winerr == NULL)
+ goto error;
+ if (PyObject_SetAttrString(py_exc, "winerror", py_winerr) != 0)
+ goto error;
+ PyErr_SetObject(PyExc_OSError, py_exc);
+ Py_XDECREF(py_exc);
+ return NULL;
+
+error:
+ Py_XDECREF(py_exc);
+ Py_XDECREF(py_winerr);
+ return NULL;
+}
+#endif // !defined(PyErr_SetFromWindowsErrWithFilename)
+
+
+// PyPy 2.7
+#if !defined(PyErr_SetFromWindowsErr)
+PyObject *
+PyErr_SetFromWindowsErr(int winerr) {
+ return PyErr_SetFromWindowsErrWithFilename(winerr, "");
+}
+#endif // !defined(PyErr_SetFromWindowsErr)
+#endif // defined(PSUTIL_WINDOWS) && defined(PYPY_VERSION)
+
+
+// ====================================================================
+// --- Custom exceptions
+// ====================================================================
+
+/*
+ * Same as PyErr_SetFromErrno(0) but adds the syscall to the exception
+ * message.
+ */
+PyObject *
+PyErr_SetFromOSErrnoWithSyscall(const char *syscall) {
+ char fullmsg[1024];
+
+#ifdef PSUTIL_WINDOWS
+ sprintf(fullmsg, "(originated from %s)", syscall);
+ PyErr_SetFromWindowsErrWithFilename(GetLastError(), fullmsg);
+#else
+ PyObject *exc;
+ sprintf(fullmsg, "%s (originated from %s)", strerror(errno), syscall);
+ exc = PyObject_CallFunction(PyExc_OSError, "(is)", errno, fullmsg);
+ PyErr_SetObject(PyExc_OSError, exc);
+ Py_XDECREF(exc);
+#endif
+ return NULL;
+}
+
+
+/*
+ * Set OSError(errno=ESRCH, strerror="No such process (originated from")
+ * Python exception.
+ */
+PyObject *
+NoSuchProcess(const char *syscall) {
+ PyObject *exc;
+ char msg[1024];
+
+ sprintf(msg, "assume no such process (originated from %s)", syscall);
+ exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg);
+ PyErr_SetObject(PyExc_OSError, exc);
+ Py_XDECREF(exc);
+ return NULL;
+}
+
+
+/*
+ * Set OSError(errno=EACCES, strerror="Permission denied" (originated from ...)
+ * Python exception.
+ */
+PyObject *
+AccessDenied(const char *syscall) {
+ PyObject *exc;
+ char msg[1024];
+
+ sprintf(msg, "assume access denied (originated from %s)", syscall);
+ exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg);
+ PyErr_SetObject(PyExc_OSError, exc);
+ Py_XDECREF(exc);
+ 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.
+ */
+PyObject *
+psutil_set_testing(PyObject *self, PyObject *args) {
+ PSUTIL_TESTING = 1;
+ PSUTIL_DEBUG = 1;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+/*
+ * 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);
+ }
+}
+
+
+/*
+ * Called on module import on all platforms.
+ */
+int
+psutil_setup(void) {
+ if (getenv("PSUTIL_DEBUG") != NULL)
+ PSUTIL_DEBUG = 1;
+ if (getenv("PSUTIL_TESTING") != NULL)
+ PSUTIL_TESTING = 1;
+ return 0;
+}
+
+
+// ============================================================================
+// Utility functions (BSD)
+// ============================================================================
+
+#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
+void
+convert_kvm_err(const char *syscall, char *errbuf) {
+ char fullmsg[8192];
+
+ sprintf(fullmsg, "(originated from %s: %s)", syscall, errbuf);
+ if (strstr(errbuf, "Permission denied") != NULL)
+ AccessDenied(fullmsg);
+ else if (strstr(errbuf, "Operation not permitted") != NULL)
+ AccessDenied(fullmsg);
+ else
+ PyErr_Format(PyExc_RuntimeError, fullmsg);
+}
+#endif
+
+
+// ====================================================================
+// --- Windows
+// ====================================================================
+
+#ifdef PSUTIL_WINDOWS
+#include <windows.h>
+
+// Needed to make these globally visible.
+int PSUTIL_WINVER;
+SYSTEM_INFO PSUTIL_SYSTEM_INFO;
+CRITICAL_SECTION PSUTIL_CRITICAL_SECTION;
+
+
+// A wrapper around GetModuleHandle and GetProcAddress.
+PVOID
+psutil_GetProcAddress(LPCSTR libname, LPCSTR procname) {
+ HMODULE mod;
+ FARPROC addr;
+
+ if ((mod = GetModuleHandleA(libname)) == NULL) {
+ PyErr_SetFromWindowsErrWithFilename(0, libname);
+ return NULL;
+ }
+ if ((addr = GetProcAddress(mod, procname)) == NULL) {
+ PyErr_SetFromWindowsErrWithFilename(0, procname);
+ return NULL;
+ }
+ return addr;
+}
+
+
+// A wrapper around LoadLibrary and GetProcAddress.
+PVOID
+psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname) {
+ HMODULE mod;
+ FARPROC addr;
+
+ Py_BEGIN_ALLOW_THREADS
+ mod = LoadLibraryA(libname);
+ Py_END_ALLOW_THREADS
+ if (mod == NULL) {
+ PyErr_SetFromWindowsErrWithFilename(0, libname);
+ return NULL;
+ }
+ if ((addr = GetProcAddress(mod, procname)) == NULL) {
+ PyErr_SetFromWindowsErrWithFilename(0, procname);
+ FreeLibrary(mod);
+ return NULL;
+ }
+ // Causes crash.
+ // FreeLibrary(mod);
+ return addr;
+}
+
+
+/*
+ * Convert a NTSTATUS value to a Win32 error code and set the proper
+ * Python exception.
+ */
+PVOID
+psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall) {
+ ULONG err;
+ char fullmsg[1024];
+
+ if (NT_NTWIN32(Status))
+ err = WIN32_FROM_NTSTATUS(Status);
+ else
+ err = RtlNtStatusToDosErrorNoTeb(Status);
+ // if (GetLastError() != 0)
+ // err = GetLastError();
+ sprintf(fullmsg, "(originated from %s)", syscall);
+ return PyErr_SetFromWindowsErrWithFilename(err, fullmsg);
+}
+
+
+static int
+psutil_loadlibs() {
+ // --- Mandatory
+ NtQuerySystemInformation = psutil_GetProcAddressFromLib(
+ "ntdll.dll", "NtQuerySystemInformation");
+ if (! NtQuerySystemInformation)
+ return 1;
+ NtQueryInformationProcess = psutil_GetProcAddress(
+ "ntdll.dll", "NtQueryInformationProcess");
+ if (! NtQueryInformationProcess)
+ return 1;
+ NtSetInformationProcess = psutil_GetProcAddress(
+ "ntdll.dll", "NtSetInformationProcess");
+ if (! NtSetInformationProcess)
+ return 1;
+ NtQueryObject = psutil_GetProcAddressFromLib(
+ "ntdll.dll", "NtQueryObject");
+ if (! NtQueryObject)
+ return 1;
+ RtlIpv4AddressToStringA = psutil_GetProcAddressFromLib(
+ "ntdll.dll", "RtlIpv4AddressToStringA");
+ if (! RtlIpv4AddressToStringA)
+ return 1;
+ GetExtendedTcpTable = psutil_GetProcAddressFromLib(
+ "iphlpapi.dll", "GetExtendedTcpTable");
+ if (! GetExtendedTcpTable)
+ return 1;
+ GetExtendedUdpTable = psutil_GetProcAddressFromLib(
+ "iphlpapi.dll", "GetExtendedUdpTable");
+ if (! GetExtendedUdpTable)
+ return 1;
+ RtlGetVersion = psutil_GetProcAddressFromLib(
+ "ntdll.dll", "RtlGetVersion");
+ if (! RtlGetVersion)
+ return 1;
+ NtSuspendProcess = psutil_GetProcAddressFromLib(
+ "ntdll", "NtSuspendProcess");
+ if (! NtSuspendProcess)
+ return 1;
+ NtResumeProcess = psutil_GetProcAddressFromLib(
+ "ntdll", "NtResumeProcess");
+ if (! NtResumeProcess)
+ return 1;
+ NtQueryVirtualMemory = psutil_GetProcAddressFromLib(
+ "ntdll", "NtQueryVirtualMemory");
+ if (! NtQueryVirtualMemory)
+ return 1;
+ RtlNtStatusToDosErrorNoTeb = psutil_GetProcAddressFromLib(
+ "ntdll", "RtlNtStatusToDosErrorNoTeb");
+ if (! RtlNtStatusToDosErrorNoTeb)
+ return 1;
+ GetTickCount64 = psutil_GetProcAddress(
+ "kernel32", "GetTickCount64");
+ if (! GetTickCount64)
+ return 1;
+ RtlIpv6AddressToStringA = psutil_GetProcAddressFromLib(
+ "ntdll.dll", "RtlIpv6AddressToStringA");
+ if (! RtlIpv6AddressToStringA)
+ return 1;
+
+ // --- Optional
+ // minimum requirement: Win 7
+ GetActiveProcessorCount = psutil_GetProcAddress(
+ "kernel32", "GetActiveProcessorCount");
+ // minumum requirement: Win 7
+ GetLogicalProcessorInformationEx = psutil_GetProcAddressFromLib(
+ "kernel32", "GetLogicalProcessorInformationEx");
+ // minimum requirements: Windows Server Core
+ WTSEnumerateSessionsW = psutil_GetProcAddressFromLib(
+ "wtsapi32.dll", "WTSEnumerateSessionsW");
+ WTSQuerySessionInformationW = psutil_GetProcAddressFromLib(
+ "wtsapi32.dll", "WTSQuerySessionInformationW");
+ WTSFreeMemory = psutil_GetProcAddressFromLib(
+ "wtsapi32.dll", "WTSFreeMemory");
+
+ PyErr_Clear();
+ return 0;
+}
+
+
+static int
+psutil_set_winver() {
+ RTL_OSVERSIONINFOEXW versionInfo;
+ ULONG maj;
+ ULONG min;
+
+ versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
+ memset(&versionInfo, 0, sizeof(RTL_OSVERSIONINFOEXW));
+ RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo);
+ maj = versionInfo.dwMajorVersion;
+ min = versionInfo.dwMinorVersion;
+ if (maj == 6 && min == 0)
+ PSUTIL_WINVER = PSUTIL_WINDOWS_VISTA; // or Server 2008
+ else if (maj == 6 && min == 1)
+ PSUTIL_WINVER = PSUTIL_WINDOWS_7;
+ else if (maj == 6 && min == 2)
+ PSUTIL_WINVER = PSUTIL_WINDOWS_8;
+ else if (maj == 6 && min == 3)
+ PSUTIL_WINVER = PSUTIL_WINDOWS_8_1;
+ else if (maj == 10 && min == 0)
+ PSUTIL_WINVER = PSUTIL_WINDOWS_10;
+ else
+ PSUTIL_WINVER = PSUTIL_WINDOWS_NEW;
+ return 0;
+}
+
+
+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.
+ * A FILETIME contains a 64-bit value representing the number of
+ * 100-nanosecond intervals since January 1, 1601 (UTC).
+ * A UNIX time is the number of seconds that have elapsed since the
+ * UNIX epoch, that is the time 00:00:00 UTC on 1 January 1970.
+ */
+static double
+_to_unix_time(ULONGLONG hiPart, ULONGLONG loPart) {
+ ULONGLONG ret;
+
+ // 100 nanosecond intervals since January 1, 1601.
+ ret = hiPart << 32;
+ ret += loPart;
+ // Change starting time to the Epoch (00:00:00 UTC, January 1, 1970).
+ ret -= 116444736000000000ull;
+ // Convert nano secs to secs.
+ return (double) ret / 10000000ull;
+}
+
+
+double
+psutil_FiletimeToUnixTime(FILETIME ft) {
+ return _to_unix_time((ULONGLONG)ft.dwHighDateTime,
+ (ULONGLONG)ft.dwLowDateTime);
+}
+
+
+double
+psutil_LargeIntegerToUnixTime(LARGE_INTEGER li) {
+ return _to_unix_time((ULONGLONG)li.HighPart,
+ (ULONGLONG)li.LowPart);
+}
+#endif // PSUTIL_WINDOWS
diff --git a/contrib/python/psutil/py3/psutil/_psutil_common.h b/contrib/python/psutil/py3/psutil/_psutil_common.h
new file mode 100644
index 0000000000..cb0b399d8e
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_psutil_common.h
@@ -0,0 +1,158 @@
+/*
+ * 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>
+
+// ====================================================================
+// --- 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;
+
+// strncpy() variant which appends a null terminator.
+#define PSUTIL_STRNCPY(dst, src, n) \
+ strncpy(dst, src, n - 1); \
+ dst[n - 1] = '\0'
+
+// ====================================================================
+// --- Backward compatibility with missing Python.h APIs
+// ====================================================================
+
+#if PY_MAJOR_VERSION < 3
+ // On Python 2 we just return a plain byte string, which is never
+ // supposed to raise decoding errors, see:
+ // https://github.com/giampaolo/psutil/issues/1040
+ #define PyUnicode_DecodeFSDefault PyString_FromString
+ #define PyUnicode_DecodeFSDefaultAndSize PyString_FromStringAndSize
+#endif
+
+#if defined(PSUTIL_WINDOWS) && \
+ defined(PYPY_VERSION) && \
+ !defined(PyErr_SetFromWindowsErrWithFilename)
+ PyObject *PyErr_SetFromWindowsErrWithFilename(int ierr,
+ const char *filename);
+#endif
+
+// --- _Py_PARSE_PID
+
+// SIZEOF_INT|LONG is missing on Linux + PyPy (only?).
+// SIZEOF_PID_T is missing on Windows + Python2.
+// In this case we guess it from setup.py. It's not 100% bullet proof,
+// If wrong we'll probably get compiler warnings.
+// FWIW on all UNIX platforms I've seen pid_t is defined as an int.
+// _getpid() on Windows also returns an int.
+#if !defined(SIZEOF_INT)
+ #define SIZEOF_INT 4
+#endif
+#if !defined(SIZEOF_LONG)
+ #define SIZEOF_LONG 8
+#endif
+#if !defined(SIZEOF_PID_T)
+ #define SIZEOF_PID_T PSUTIL_SIZEOF_PID_T // set as a macro in setup.py
+#endif
+
+// _Py_PARSE_PID is Python 3 only, but since it's private make sure it's
+// always present.
+#ifndef _Py_PARSE_PID
+ #if SIZEOF_PID_T == SIZEOF_INT
+ #define _Py_PARSE_PID "i"
+ #elif SIZEOF_PID_T == SIZEOF_LONG
+ #define _Py_PARSE_PID "l"
+ #elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG
+ #define _Py_PARSE_PID "L"
+ #else
+ #error "_Py_PARSE_PID: sizeof(pid_t) is neither sizeof(int), "
+ "sizeof(long) or sizeof(long long)"
+ #endif
+#endif
+
+// Python 2 or PyPy on Windows
+#ifndef PyLong_FromPid
+ #if ((SIZEOF_PID_T == SIZEOF_INT) || (SIZEOF_PID_T == SIZEOF_LONG))
+ #if PY_MAJOR_VERSION >= 3
+ #define PyLong_FromPid PyLong_FromLong
+ #else
+ #define PyLong_FromPid PyInt_FromLong
+ #endif
+ #elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG
+ #define PyLong_FromPid PyLong_FromLongLong
+ #else
+ #error "PyLong_FromPid: sizeof(pid_t) is neither sizeof(int), "
+ "sizeof(long) or sizeof(long long)"
+ #endif
+#endif
+
+// ====================================================================
+// --- Custom exceptions
+// ====================================================================
+
+PyObject* AccessDenied(const char *msg);
+PyObject* NoSuchProcess(const char *msg);
+PyObject* PyErr_SetFromOSErrnoWithSyscall(const char *syscall);
+
+// ====================================================================
+// --- Global utils
+// ====================================================================
+
+PyObject* psutil_set_testing(PyObject *self, PyObject *args);
+void psutil_debug(const char* format, ...);
+int psutil_setup(void);
+
+// ====================================================================
+// --- BSD
+// ====================================================================
+
+void convert_kvm_err(const char *syscall, char *errbuf);
+
+// ====================================================================
+// --- Windows
+// ====================================================================
+
+#ifdef PSUTIL_WINDOWS
+ #include <windows.h>
+ // make it available to any file which includes this module
+ #include "arch/windows/ntextapi.h"
+
+ extern int PSUTIL_WINVER;
+ extern SYSTEM_INFO PSUTIL_SYSTEM_INFO;
+ extern CRITICAL_SECTION PSUTIL_CRITICAL_SECTION;
+
+ #define PSUTIL_WINDOWS_VISTA 60
+ #define PSUTIL_WINDOWS_7 61
+ #define PSUTIL_WINDOWS_8 62
+ #define PSUTIL_WINDOWS_8_1 63
+ #define PSUTIL_WINDOWS_10 100
+ #define PSUTIL_WINDOWS_NEW MAXLONG
+
+ #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
+ #define MALLOC_ZERO(x) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (x))
+ #define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
+
+ #define _NT_FACILITY_MASK 0xfff
+ #define _NT_FACILITY_SHIFT 16
+ #define _NT_FACILITY(status) \
+ ((((ULONG)(status)) >> _NT_FACILITY_SHIFT) & _NT_FACILITY_MASK)
+
+ #define NT_NTWIN32(status) (_NT_FACILITY(status) == FACILITY_WIN32)
+ #define WIN32_FROM_NTSTATUS(status) (((ULONG)(status)) & 0xffff)
+
+ #define LO_T 1e-7
+ #define HI_T 429.4967296
+
+ #ifndef AF_INET6
+ #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);
+ double psutil_FiletimeToUnixTime(FILETIME ft);
+ double psutil_LargeIntegerToUnixTime(LARGE_INTEGER li);
+#endif
diff --git a/contrib/python/psutil/py3/psutil/_psutil_linux.c b/contrib/python/psutil/py3/psutil/_psutil_linux.c
new file mode 100644
index 0000000000..5836cd6b78
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_psutil_linux.c
@@ -0,0 +1,555 @@
+/*
+ * 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.
+ *
+ * Linux-specific functions.
+ */
+
+#ifndef _GNU_SOURCE
+ #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 "_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;
+
+ 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"},
+#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."},
+#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"},
+
+ // --- linux specific
+
+ {"linux_sysinfo", psutil_linux_sysinfo, METH_VARARGS,
+ "A wrapper around sysinfo(), return system memory usage statistics"},
+ // --- others
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
+
+ {NULL, NULL, 0, NULL}
+};
+
+
+#if PY_MAJOR_VERSION >= 3
+ #define INITERR return NULL
+
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "_psutil_linux",
+ NULL,
+ -1,
+ mod_methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ PyObject *PyInit__psutil_linux(void)
+#else /* PY_MAJOR_VERSION */
+ #define INITERR return
+
+ void init_psutil_linux(void)
+#endif /* PY_MAJOR_VERSION */
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *mod = PyModule_Create(&moduledef);
+#else
+ PyObject *mod = Py_InitModule("_psutil_linux", mod_methods);
+#endif
+ if (mod == NULL)
+ INITERR;
+
+ if (PyModule_AddIntConstant(mod, "version", PSUTIL_VERSION)) INITERR;
+ if (PyModule_AddIntConstant(mod, "DUPLEX_HALF", DUPLEX_HALF)) INITERR;
+ if (PyModule_AddIntConstant(mod, "DUPLEX_FULL", DUPLEX_FULL)) INITERR;
+ if (PyModule_AddIntConstant(mod, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN)) INITERR;
+
+ psutil_setup();
+
+ if (mod == NULL)
+ INITERR;
+#if PY_MAJOR_VERSION >= 3
+ return mod;
+#endif
+}
diff --git a/contrib/python/psutil/py3/psutil/_psutil_osx.c b/contrib/python/psutil/py3/psutil/_psutil_osx.c
new file mode 100644
index 0000000000..13d0bb6856
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_psutil_osx.c
@@ -0,0 +1,1914 @@
+/*
+ * 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.
+ *
+ * macOS platform-specific module methods.
+ */
+
+#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 <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);
+
+ 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"},
+
+ // --- 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."},
+
+ // --- others
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
+
+ {NULL, NULL, 0, NULL}
+};
+
+
+#if PY_MAJOR_VERSION >= 3
+ #define INITERR return NULL
+
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "_psutil_osx",
+ NULL,
+ -1,
+ mod_methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ PyObject *PyInit__psutil_osx(void)
+#else /* PY_MAJOR_VERSION */
+ #define INITERR return
+
+ void init_psutil_osx(void)
+#endif /* PY_MAJOR_VERSION */
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *mod = PyModule_Create(&moduledef);
+#else
+ PyObject *mod = Py_InitModule("_psutil_osx", mod_methods);
+#endif
+ if (mod == NULL)
+ INITERR;
+
+ if (psutil_setup() != 0)
+ INITERR;
+
+ if (PyModule_AddIntConstant(mod, "version", PSUTIL_VERSION))
+ INITERR;
+ // process status constants, defined in:
+ // http://fxr.watson.org/fxr/source/bsd/sys/proc.h?v=xnu-792.6.70#L149
+ if (PyModule_AddIntConstant(mod, "SIDL", SIDL))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "SRUN", SRUN))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "SSLEEP", SSLEEP))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "SSTOP", SSTOP))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "SZOMB", SZOMB))
+ INITERR;
+ // connection status constants
+ if (PyModule_AddIntConstant(mod, "TCPS_CLOSED", TCPS_CLOSED))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_CLOSING", TCPS_CLOSING))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_LISTEN", TCPS_LISTEN))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_ESTABLISHED", TCPS_ESTABLISHED))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_SYN_SENT", TCPS_SYN_SENT))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_SYN_RECEIVED", TCPS_SYN_RECEIVED))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_LAST_ACK", TCPS_LAST_ACK))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_TIME_WAIT", TCPS_TIME_WAIT))
+ INITERR;
+ 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
+ return mod;
+#endif
+}
diff --git a/contrib/python/psutil/py3/psutil/_psutil_posix.c b/contrib/python/psutil/py3/psutil/_psutil_posix.c
new file mode 100644
index 0000000000..305cec76d1
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_psutil_posix.c
@@ -0,0 +1,829 @@
+/*
+ * 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.
+ *
+ * Functions specific to all POSIX compliant platforms.
+ */
+
+#include <Python.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <unistd.h>
+
+#ifdef PSUTIL_SUNOS10
+ #include "arch/solaris/v10/ifaddrs.h"
+#elif PSUTIL_AIX
+ #include "arch/aix/ifaddrs.h"
+#else
+ #include <ifaddrs.h>
+#endif
+
+#if defined(PSUTIL_LINUX)
+ #include <netdb.h>
+ #include <linux/types.h>
+ #include <linux/if_packet.h>
+#endif
+#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
+ #include <netdb.h>
+ #include <netinet/in.h>
+ #include <net/if_dl.h>
+ #include <sys/sockio.h>
+ #include <net/if_media.h>
+ #include <net/if.h>
+#endif
+#if defined(PSUTIL_SUNOS)
+ #include <netdb.h>
+ #include <sys/sockio.h>
+#endif
+#if defined(PSUTIL_AIX)
+ #include <netdb.h>
+#endif
+#if defined(PSUTIL_LINUX) || defined(PSUTIL_FREEBSD)
+ #include <sys/resource.h>
+#endif
+
+#include "_psutil_common.h"
+
+
+// ====================================================================
+// --- Utils
+// ====================================================================
+
+
+/*
+ * From "man getpagesize" on Linux, https://linux.die.net/man/2/getpagesize:
+ *
+ * > In SUSv2 the getpagesize() call is labeled LEGACY, and in POSIX.1-2001
+ * > it has been dropped.
+ * > Portable applications should employ sysconf(_SC_PAGESIZE) instead
+ * > of getpagesize().
+ * > Most systems allow the synonym _SC_PAGE_SIZE for _SC_PAGESIZE.
+ * > Whether getpagesize() is present as a Linux system call depends on the
+ * > architecture.
+ */
+long
+psutil_getpagesize(void) {
+#ifdef _SC_PAGESIZE
+ // recommended POSIX
+ return sysconf(_SC_PAGESIZE);
+#elif _SC_PAGE_SIZE
+ // alias
+ return sysconf(_SC_PAGE_SIZE);
+#else
+ // legacy
+ return (long) getpagesize();
+#endif
+}
+
+
+/*
+ * Check if PID exists. Return values:
+ * 1: exists
+ * 0: does not exist
+ * -1: error (Python exception is set)
+ */
+int
+psutil_pid_exists(pid_t pid) {
+ int ret;
+
+ // No negative PID exists, plus -1 is an alias for sending signal
+ // too all processes except system ones. Not what we want.
+ if (pid < 0)
+ return 0;
+
+ // As per "man 2 kill" PID 0 is an alias for sending the signal to
+ // every process in the process group of the calling process.
+ // Not what we want. Some platforms have PID 0, some do not.
+ // We decide that at runtime.
+ if (pid == 0) {
+#if defined(PSUTIL_LINUX) || defined(PSUTIL_FREEBSD)
+ return 0;
+#else
+ return 1;
+#endif
+ }
+
+ ret = kill(pid , 0);
+ if (ret == 0)
+ return 1;
+ else {
+ if (errno == ESRCH) {
+ // ESRCH == No such process
+ return 0;
+ }
+ else if (errno == EPERM) {
+ // EPERM clearly indicates there's a process to deny
+ // access to.
+ return 1;
+ }
+ else {
+ // According to "man 2 kill" possible error values are
+ // (EINVAL, EPERM, ESRCH) therefore we should never get
+ // here. If we do let's be explicit in considering this
+ // an error.
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ }
+}
+
+
+/*
+ * Utility used for those syscalls which do not return a meaningful
+ * error that we can translate into an exception which makes sense.
+ * As such, we'll have to guess.
+ * On UNIX, if errno is set, we return that one (OSError).
+ * Else, if PID does not exist we assume the syscall failed because
+ * of that so we raise NoSuchProcess.
+ * If none of this is true we giveup and raise RuntimeError(msg).
+ * This will always set a Python exception and return NULL.
+ */
+void
+psutil_raise_for_pid(long pid, char *syscall) {
+ if (errno != 0) // unlikely
+ PyErr_SetFromOSErrnoWithSyscall(syscall);
+ else if (psutil_pid_exists(pid) == 0)
+ NoSuchProcess(syscall);
+ else
+ PyErr_Format(PyExc_RuntimeError, "%s syscall failed", syscall);
+}
+
+
+// ====================================================================
+// --- Python wrappers
+// ====================================================================
+
+
+// Exposed so we can test it against Python's stdlib.
+static PyObject *
+psutil_getpagesize_pywrapper(PyObject *self, PyObject *args) {
+ return Py_BuildValue("l", psutil_getpagesize());
+}
+
+
+/*
+ * Given a PID return process priority as a Python integer.
+ */
+static PyObject *
+psutil_posix_getpriority(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int priority;
+ errno = 0;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+
+#ifdef PSUTIL_OSX
+ priority = getpriority(PRIO_PROCESS, (id_t)pid);
+#else
+ priority = getpriority(PRIO_PROCESS, pid);
+#endif
+ if (errno != 0)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ return Py_BuildValue("i", priority);
+}
+
+
+/*
+ * Given a PID and a value change process priority.
+ */
+static PyObject *
+psutil_posix_setpriority(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int priority;
+ int retval;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i", &pid, &priority))
+ return NULL;
+
+#ifdef PSUTIL_OSX
+ retval = setpriority(PRIO_PROCESS, (id_t)pid, priority);
+#else
+ retval = setpriority(PRIO_PROCESS, pid, priority);
+#endif
+ if (retval == -1)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ Py_RETURN_NONE;
+}
+
+
+/*
+ * Translate a sockaddr struct into a Python string.
+ * Return None if address family is not AF_INET* or AF_PACKET.
+ */
+static PyObject *
+psutil_convert_ipaddr(struct sockaddr *addr, int family) {
+ char buf[NI_MAXHOST];
+ int err;
+ int addrlen;
+ size_t n;
+ size_t len;
+ const char *data;
+ char *ptr;
+
+ if (addr == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ else if (family == AF_INET || family == AF_INET6) {
+ if (family == AF_INET)
+ addrlen = sizeof(struct sockaddr_in);
+ else
+ addrlen = sizeof(struct sockaddr_in6);
+ err = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,
+ NI_NUMERICHOST);
+ if (err != 0) {
+ // XXX we get here on FreeBSD when processing 'lo' / AF_INET6
+ // broadcast. Not sure what to do other than returning None.
+ // ifconfig does not show anything BTW.
+ // PyErr_Format(PyExc_RuntimeError, gai_strerror(err));
+ // return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ else {
+ return Py_BuildValue("s", buf);
+ }
+ }
+#ifdef PSUTIL_LINUX
+ else if (family == AF_PACKET) {
+ struct sockaddr_ll *lladdr = (struct sockaddr_ll *)addr;
+ len = lladdr->sll_halen;
+ data = (const char *)lladdr->sll_addr;
+ }
+#elif defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
+ else if (addr->sa_family == AF_LINK) {
+ // Note: prior to Python 3.4 socket module does not expose
+ // AF_LINK so we'll do.
+ struct sockaddr_dl *dladdr = (struct sockaddr_dl *)addr;
+ len = dladdr->sdl_alen;
+ data = LLADDR(dladdr);
+ }
+#endif
+ else {
+ // unknown family
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ // AF_PACKET or AF_LINK
+ if (len > 0) {
+ ptr = buf;
+ for (n = 0; n < len; ++n) {
+ sprintf(ptr, "%02x:", data[n] & 0xff);
+ ptr += 3;
+ }
+ *--ptr = '\0';
+ return Py_BuildValue("s", buf);
+ }
+ else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+
+
+/*
+ * Return NICs information a-la ifconfig as a list of tuples.
+ * TODO: on Solaris we won't get any MAC address.
+ */
+static PyObject*
+psutil_net_if_addrs(PyObject* self, PyObject* args) {
+ struct ifaddrs *ifaddr, *ifa;
+ int family;
+
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_address = NULL;
+ PyObject *py_netmask = NULL;
+ PyObject *py_broadcast = NULL;
+ PyObject *py_ptp = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (getifaddrs(&ifaddr) == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if (!ifa->ifa_addr)
+ continue;
+ family = ifa->ifa_addr->sa_family;
+ py_address = psutil_convert_ipaddr(ifa->ifa_addr, family);
+ // If the primary address can't be determined just skip it.
+ // I've never seen this happen on Linux but I did on FreeBSD.
+ if (py_address == Py_None)
+ continue;
+ if (py_address == NULL)
+ goto error;
+ py_netmask = psutil_convert_ipaddr(ifa->ifa_netmask, family);
+ if (py_netmask == NULL)
+ goto error;
+
+ if (ifa->ifa_flags & IFF_BROADCAST) {
+ py_broadcast = psutil_convert_ipaddr(ifa->ifa_broadaddr, family);
+ Py_INCREF(Py_None);
+ py_ptp = Py_None;
+ }
+ else if (ifa->ifa_flags & IFF_POINTOPOINT) {
+ py_ptp = psutil_convert_ipaddr(ifa->ifa_dstaddr, family);
+ Py_INCREF(Py_None);
+ py_broadcast = Py_None;
+ }
+ else {
+ Py_INCREF(Py_None);
+ Py_INCREF(Py_None);
+ py_broadcast = Py_None;
+ py_ptp = Py_None;
+ }
+
+ if ((py_broadcast == NULL) || (py_ptp == NULL))
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(siOOOO)",
+ ifa->ifa_name,
+ family,
+ py_address,
+ py_netmask,
+ py_broadcast,
+ py_ptp
+ );
+
+ if (! py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ Py_CLEAR(py_address);
+ Py_CLEAR(py_netmask);
+ Py_CLEAR(py_broadcast);
+ Py_CLEAR(py_ptp);
+ }
+
+ freeifaddrs(ifaddr);
+ return py_retlist;
+
+error:
+ if (ifaddr != NULL)
+ freeifaddrs(ifaddr);
+ Py_DECREF(py_retlist);
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_address);
+ Py_XDECREF(py_netmask);
+ Py_XDECREF(py_broadcast);
+ Py_XDECREF(py_ptp);
+ return NULL;
+}
+
+
+/*
+ * Return NIC MTU. References:
+ * http://www.i-scream.org/libstatgrab/
+ */
+static PyObject *
+psutil_net_if_mtu(PyObject *self, PyObject *args) {
+ char *nic_name;
+ int sock = -1;
+ int ret;
+#ifdef PSUTIL_SUNOS10
+ struct lifreq lifr;
+#else
+ struct ifreq ifr;
+#endif
+
+ if (! PyArg_ParseTuple(args, "s", &nic_name))
+ return NULL;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ goto error;
+
+#ifdef PSUTIL_SUNOS10
+ PSUTIL_STRNCPY(lifr.lifr_name, nic_name, sizeof(lifr.lifr_name));
+ ret = ioctl(sock, SIOCGIFMTU, &lifr);
+#else
+ PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+ ret = ioctl(sock, SIOCGIFMTU, &ifr);
+#endif
+ if (ret == -1)
+ goto error;
+ close(sock);
+
+#ifdef PSUTIL_SUNOS10
+ return Py_BuildValue("i", lifr.lifr_mtu);
+#else
+ return Py_BuildValue("i", ifr.ifr_mtu);
+#endif
+
+error:
+ if (sock != -1)
+ close(sock);
+ return PyErr_SetFromErrno(PyExc_OSError);
+}
+
+
+/*
+ * Inspect NIC flags, returns a bool indicating whether the NIC is
+ * running. References:
+ * http://www.i-scream.org/libstatgrab/
+ */
+static PyObject *
+psutil_net_if_is_running(PyObject *self, PyObject *args) {
+ char *nic_name;
+ int sock = -1;
+ int ret;
+ struct ifreq ifr;
+
+ if (! PyArg_ParseTuple(args, "s", &nic_name))
+ return NULL;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ goto error;
+
+ PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+ ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
+ if (ret == -1)
+ goto error;
+
+ close(sock);
+ if ((ifr.ifr_flags & IFF_RUNNING) != 0)
+ return Py_BuildValue("O", Py_True);
+ else
+ return Py_BuildValue("O", Py_False);
+
+error:
+ if (sock != -1)
+ close(sock);
+ return PyErr_SetFromErrno(PyExc_OSError);
+}
+
+
+/*
+ * net_if_stats() macOS/BSD implementation.
+ */
+#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
+
+int psutil_get_nic_speed(int ifm_active) {
+ // Determine NIC speed. Taken from:
+ // http://www.i-scream.org/libstatgrab/
+ // Assuming only ETHER devices
+ switch(IFM_TYPE(ifm_active)) {
+ case IFM_ETHER:
+ switch(IFM_SUBTYPE(ifm_active)) {
+#if defined(IFM_HPNA_1) && ((!defined(IFM_10G_LR)) \
+ || (IFM_10G_LR != IFM_HPNA_1))
+ // HomePNA 1.0 (1Mb/s)
+ case(IFM_HPNA_1):
+ return 1;
+#endif
+ // 10 Mbit
+ case(IFM_10_T): // 10BaseT - RJ45
+ case(IFM_10_2): // 10Base2 - Thinnet
+ case(IFM_10_5): // 10Base5 - AUI
+ case(IFM_10_STP): // 10BaseT over shielded TP
+ case(IFM_10_FL): // 10baseFL - Fiber
+ return 10;
+ // 100 Mbit
+ case(IFM_100_TX): // 100BaseTX - RJ45
+ case(IFM_100_FX): // 100BaseFX - Fiber
+ case(IFM_100_T4): // 100BaseT4 - 4 pair cat 3
+ case(IFM_100_VG): // 100VG-AnyLAN
+ case(IFM_100_T2): // 100BaseT2
+ return 100;
+ // 1000 Mbit
+ case(IFM_1000_SX): // 1000BaseSX - multi-mode fiber
+ case(IFM_1000_LX): // 1000baseLX - single-mode fiber
+ case(IFM_1000_CX): // 1000baseCX - 150ohm STP
+#if defined(IFM_1000_TX) && !defined(PSUTIL_OPENBSD)
+ // 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
+ case(IFM_1000_T):
+#endif
+ return 1000;
+#if defined(IFM_10G_SR) || defined(IFM_10G_LR) || defined(IFM_10G_CX4) \
+ || defined(IFM_10G_T)
+#ifdef IFM_10G_SR
+ case(IFM_10G_SR):
+#endif
+#ifdef IFM_10G_LR
+ case(IFM_10G_LR):
+#endif
+#ifdef IFM_10G_CX4
+ case(IFM_10G_CX4):
+#endif
+#ifdef IFM_10G_TWINAX
+ case(IFM_10G_TWINAX):
+#endif
+#ifdef IFM_10G_TWINAX_LONG
+ case(IFM_10G_TWINAX_LONG):
+#endif
+#ifdef IFM_10G_T
+ case(IFM_10G_T):
+#endif
+ return 10000;
+#endif
+#if defined(IFM_2500_SX)
+#ifdef IFM_2500_SX
+ case(IFM_2500_SX):
+#endif
+ return 2500;
+#endif // any 2.5GBit stuff...
+ // We don't know what it is
+ default:
+ return 0;
+ }
+ break;
+
+#ifdef IFM_TOKEN
+ case IFM_TOKEN:
+ switch(IFM_SUBTYPE(ifm_active)) {
+ case IFM_TOK_STP4: // Shielded twisted pair 4m - DB9
+ case IFM_TOK_UTP4: // Unshielded twisted pair 4m - RJ45
+ return 4;
+ case IFM_TOK_STP16: // Shielded twisted pair 16m - DB9
+ case IFM_TOK_UTP16: // Unshielded twisted pair 16m - RJ45
+ return 16;
+#if defined(IFM_TOK_STP100) || defined(IFM_TOK_UTP100)
+#ifdef IFM_TOK_STP100
+ case IFM_TOK_STP100: // Shielded twisted pair 100m - DB9
+#endif
+#ifdef IFM_TOK_UTP100
+ case IFM_TOK_UTP100: // Unshielded twisted pair 100m - RJ45
+#endif
+ return 100;
+#endif
+ // We don't know what it is
+ default:
+ return 0;
+ }
+ break;
+#endif
+
+#ifdef IFM_FDDI
+ case IFM_FDDI:
+ switch(IFM_SUBTYPE(ifm_active)) {
+ // We don't know what it is
+ default:
+ return 0;
+ }
+ break;
+#endif
+ case IFM_IEEE80211:
+ switch(IFM_SUBTYPE(ifm_active)) {
+ case IFM_IEEE80211_FH1: // Frequency Hopping 1Mbps
+ case IFM_IEEE80211_DS1: // Direct Sequence 1Mbps
+ return 1;
+ case IFM_IEEE80211_FH2: // Frequency Hopping 2Mbps
+ case IFM_IEEE80211_DS2: // Direct Sequence 2Mbps
+ return 2;
+ case IFM_IEEE80211_DS5: // Direct Sequence 5Mbps
+ return 5;
+ case IFM_IEEE80211_DS11: // Direct Sequence 11Mbps
+ return 11;
+ case IFM_IEEE80211_DS22: // Direct Sequence 22Mbps
+ return 22;
+ // We don't know what it is
+ default:
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+}
+
+
+/*
+ * Return stats about a particular network interface.
+ * References:
+ * http://www.i-scream.org/libstatgrab/
+ */
+static PyObject *
+psutil_net_if_duplex_speed(PyObject *self, PyObject *args) {
+ char *nic_name;
+ int sock = -1;
+ int ret;
+ int duplex;
+ int speed;
+ struct ifreq ifr;
+ struct ifmediareq ifmed;
+
+ if (! PyArg_ParseTuple(args, "s", &nic_name))
+ return NULL;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+
+ // speed / duplex
+ memset(&ifmed, 0, sizeof(struct ifmediareq));
+ strlcpy(ifmed.ifm_name, nic_name, sizeof(ifmed.ifm_name));
+ ret = ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmed);
+ if (ret == -1) {
+ speed = 0;
+ duplex = 0;
+ }
+ else {
+ speed = psutil_get_nic_speed(ifmed.ifm_active);
+ if ((ifmed.ifm_active | IFM_FDX) == ifmed.ifm_active)
+ duplex = 2;
+ else if ((ifmed.ifm_active | IFM_HDX) == ifmed.ifm_active)
+ duplex = 1;
+ else
+ duplex = 0;
+ }
+
+ close(sock);
+ return Py_BuildValue("[ii]", duplex, speed);
+}
+#endif // net_if_stats() macOS/BSD implementation
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * 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."},
+#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
+ {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS,
+ "Return NIC stats."},
+#endif
+ {NULL, NULL, 0, NULL}
+};
+
+
+#if PY_MAJOR_VERSION >= 3
+ #define INITERR return NULL
+
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "_psutil_posix",
+ NULL,
+ -1,
+ mod_methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ PyObject *PyInit__psutil_posix(void)
+#else /* PY_MAJOR_VERSION */
+ #define INITERR return
+
+ void init_psutil_posix(void)
+#endif /* PY_MAJOR_VERSION */
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *mod = PyModule_Create(&moduledef);
+#else
+ PyObject *mod = Py_InitModule("_psutil_posix", mod_methods);
+#endif
+ if (mod == NULL)
+ INITERR;
+
+#if defined(PSUTIL_BSD) || \
+ defined(PSUTIL_OSX) || \
+ defined(PSUTIL_SUNOS) || \
+ defined(PSUTIL_AIX)
+ if (PyModule_AddIntConstant(mod, "AF_LINK", AF_LINK)) INITERR;
+#endif
+
+#if defined(PSUTIL_LINUX) || defined(PSUTIL_FREEBSD)
+ PyObject *v;
+
+#ifdef RLIMIT_AS
+ if (PyModule_AddIntConstant(mod, "RLIMIT_AS", RLIMIT_AS)) INITERR;
+#endif
+
+#ifdef RLIMIT_CORE
+ if (PyModule_AddIntConstant(mod, "RLIMIT_CORE", RLIMIT_CORE)) INITERR;
+#endif
+
+#ifdef RLIMIT_CPU
+ if (PyModule_AddIntConstant(mod, "RLIMIT_CPU", RLIMIT_CPU)) INITERR;
+#endif
+
+#ifdef RLIMIT_DATA
+ if (PyModule_AddIntConstant(mod, "RLIMIT_DATA", RLIMIT_DATA)) INITERR;
+#endif
+
+#ifdef RLIMIT_FSIZE
+ if (PyModule_AddIntConstant(mod, "RLIMIT_FSIZE", RLIMIT_FSIZE)) INITERR;
+#endif
+
+#ifdef RLIMIT_MEMLOCK
+ if (PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", RLIMIT_MEMLOCK)) INITERR;
+#endif
+
+#ifdef RLIMIT_NOFILE
+ if (PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", RLIMIT_NOFILE)) INITERR;
+#endif
+
+#ifdef RLIMIT_NPROC
+ if (PyModule_AddIntConstant(mod, "RLIMIT_NPROC", RLIMIT_NPROC)) INITERR;
+#endif
+
+#ifdef RLIMIT_RSS
+ if (PyModule_AddIntConstant(mod, "RLIMIT_RSS", RLIMIT_RSS)) INITERR;
+#endif
+
+#ifdef RLIMIT_STACK
+ if (PyModule_AddIntConstant(mod, "RLIMIT_STACK", RLIMIT_STACK)) INITERR;
+#endif
+
+// Linux specific
+
+#ifdef RLIMIT_LOCKS
+ if (PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", RLIMIT_LOCKS)) INITERR;
+#endif
+
+#ifdef RLIMIT_MSGQUEUE
+ if (PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", RLIMIT_MSGQUEUE)) INITERR;
+#endif
+
+#ifdef RLIMIT_NICE
+ if (PyModule_AddIntConstant(mod, "RLIMIT_NICE", RLIMIT_NICE)) INITERR;
+#endif
+
+#ifdef RLIMIT_RTPRIO
+ if (PyModule_AddIntConstant(mod, "RLIMIT_RTPRIO", RLIMIT_RTPRIO)) INITERR;
+#endif
+
+#ifdef RLIMIT_RTTIME
+ if (PyModule_AddIntConstant(mod, "RLIMIT_RTTIME", RLIMIT_RTTIME)) INITERR;
+#endif
+
+#ifdef RLIMIT_SIGPENDING
+ if (PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", RLIMIT_SIGPENDING)) INITERR;
+#endif
+
+// Free specific
+
+#ifdef RLIMIT_SWAP
+ if (PyModule_AddIntConstant(mod, "RLIMIT_SWAP", RLIMIT_SWAP)) INITERR;
+#endif
+
+#ifdef RLIMIT_SBSIZE
+ if (PyModule_AddIntConstant(mod, "RLIMIT_SBSIZE", RLIMIT_SBSIZE)) INITERR;
+#endif
+
+#ifdef RLIMIT_NPTS
+ if (PyModule_AddIntConstant(mod, "RLIMIT_NPTS", RLIMIT_NPTS)) INITERR;
+#endif
+
+#if defined(HAVE_LONG_LONG)
+ if (sizeof(RLIM_INFINITY) > sizeof(long)) {
+ v = PyLong_FromLongLong((PY_LONG_LONG) RLIM_INFINITY);
+ } else
+#endif
+ {
+ v = PyLong_FromLong((long) RLIM_INFINITY);
+ }
+ if (v) {
+ PyModule_AddObject(mod, "RLIM_INFINITY", v);
+ }
+#endif // defined(PSUTIL_LINUX) || defined(PSUTIL_FREEBSD)
+
+ if (mod == NULL)
+ INITERR;
+#if PY_MAJOR_VERSION >= 3
+ return mod;
+#endif
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/contrib/python/psutil/py3/psutil/_psutil_posix.h b/contrib/python/psutil/py3/psutil/_psutil_posix.h
new file mode 100644
index 0000000000..5a37e48b15
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_psutil_posix.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.
+ */
+
+long psutil_getpagesize(void);
+int psutil_pid_exists(pid_t pid);
+void psutil_raise_for_pid(pid_t pid, char *msg);
diff --git a/contrib/python/psutil/py3/psutil/_psutil_windows.c b/contrib/python/psutil/py3/psutil/_psutil_windows.c
new file mode 100644
index 0000000000..a2154e923e
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_psutil_windows.c
@@ -0,0 +1,1852 @@
+/*
+ * 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.
+ *
+ * Windows platform-specific module methods for _psutil_windows.
+ *
+ * List of undocumented Windows NT APIs which are used in here and in
+ * other modules:
+ * - NtQuerySystemInformation
+ * - NtQueryInformationProcess
+ * - NtQueryObject
+ * - NtSuspendProcess
+ * - 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/net.h"
+#include "arch/windows/services.h"
+#include "arch/windows/socks.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 ---------------------------
+
+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"},
+
+ // --- alternative pinfo interface
+ {"proc_info", psutil_proc_info, METH_VARARGS,
+ "Various process information"},
+
+ // --- 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."},
+
+ // --- 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"},
+
+ // --- windows API bindings
+ {"win32_QueryDosDevice", psutil_win32_QueryDosDevice, METH_VARARGS,
+ "QueryDosDevice binding"},
+
+ // --- others
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
+
+ {NULL, NULL, 0, NULL}
+};
+
+
+struct module_state {
+ PyObject *error;
+};
+
+#if PY_MAJOR_VERSION >= 3
+#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
+#else
+#define GETSTATE(m) (&_state)
+static struct module_state _state;
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+
+static int psutil_windows_traverse(PyObject *m, visitproc visit, void *arg) {
+ Py_VISIT(GETSTATE(m)->error);
+ return 0;
+}
+
+static int psutil_windows_clear(PyObject *m) {
+ Py_CLEAR(GETSTATE(m)->error);
+ return 0;
+}
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "psutil_windows",
+ NULL,
+ sizeof(struct module_state),
+ PsutilMethods,
+ NULL,
+ psutil_windows_traverse,
+ psutil_windows_clear,
+ NULL
+};
+
+#define INITERROR return NULL
+
+PyMODINIT_FUNC PyInit__psutil_windows(void)
+
+#else
+#define INITERROR return
+void init_psutil_windows(void)
+#endif
+{
+ struct module_state *st = NULL;
+#if PY_MAJOR_VERSION >= 3
+ PyObject *module = PyModule_Create(&moduledef);
+#else
+ PyObject *module = Py_InitModule("_psutil_windows", PsutilMethods);
+#endif
+ if (module == NULL)
+ INITERROR;
+
+ if (psutil_setup() != 0)
+ INITERROR;
+ if (psutil_load_globals() != 0)
+ INITERROR;
+ if (psutil_set_se_debug() != 0)
+ INITERROR;
+
+ st = GETSTATE(module);
+ st->error = PyErr_NewException("_psutil_windows.Error", NULL, NULL);
+ if (st->error == NULL) {
+ Py_DECREF(module);
+ INITERROR;
+ }
+
+ // Exceptions.
+ TimeoutExpired = PyErr_NewException(
+ "_psutil_windows.TimeoutExpired", NULL, NULL);
+ Py_INCREF(TimeoutExpired);
+ PyModule_AddObject(module, "TimeoutExpired", TimeoutExpired);
+
+ TimeoutAbandoned = PyErr_NewException(
+ "_psutil_windows.TimeoutAbandoned", NULL, NULL);
+ Py_INCREF(TimeoutAbandoned);
+ PyModule_AddObject(module, "TimeoutAbandoned", TimeoutAbandoned);
+
+ // version constant
+ PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
+
+ // process status constants
+ // http://msdn.microsoft.com/en-us/library/ms683211(v=vs.85).aspx
+ PyModule_AddIntConstant(
+ module, "ABOVE_NORMAL_PRIORITY_CLASS", ABOVE_NORMAL_PRIORITY_CLASS);
+ PyModule_AddIntConstant(
+ module, "BELOW_NORMAL_PRIORITY_CLASS", BELOW_NORMAL_PRIORITY_CLASS);
+ PyModule_AddIntConstant(
+ module, "HIGH_PRIORITY_CLASS", HIGH_PRIORITY_CLASS);
+ PyModule_AddIntConstant(
+ module, "IDLE_PRIORITY_CLASS", IDLE_PRIORITY_CLASS);
+ PyModule_AddIntConstant(
+ module, "NORMAL_PRIORITY_CLASS", NORMAL_PRIORITY_CLASS);
+ PyModule_AddIntConstant(
+ module, "REALTIME_PRIORITY_CLASS", REALTIME_PRIORITY_CLASS);
+
+ // connection status constants
+ // http://msdn.microsoft.com/en-us/library/cc669305.aspx
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_CLOSED", MIB_TCP_STATE_CLOSED);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_CLOSING", MIB_TCP_STATE_CLOSING);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_CLOSE_WAIT", MIB_TCP_STATE_CLOSE_WAIT);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_LISTEN", MIB_TCP_STATE_LISTEN);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_ESTAB", MIB_TCP_STATE_ESTAB);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_SYN_SENT", MIB_TCP_STATE_SYN_SENT);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_SYN_RCVD", MIB_TCP_STATE_SYN_RCVD);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_FIN_WAIT1", MIB_TCP_STATE_FIN_WAIT1);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_FIN_WAIT2", MIB_TCP_STATE_FIN_WAIT2);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_LAST_ACK", MIB_TCP_STATE_LAST_ACK);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_TIME_WAIT", MIB_TCP_STATE_TIME_WAIT);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_TIME_WAIT", MIB_TCP_STATE_TIME_WAIT);
+ PyModule_AddIntConstant(
+ module, "MIB_TCP_STATE_DELETE_TCB", MIB_TCP_STATE_DELETE_TCB);
+ PyModule_AddIntConstant(
+ module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
+
+ // service status constants
+ /*
+ PyModule_AddIntConstant(
+ module, "SERVICE_CONTINUE_PENDING", SERVICE_CONTINUE_PENDING);
+ PyModule_AddIntConstant(
+ module, "SERVICE_PAUSE_PENDING", SERVICE_PAUSE_PENDING);
+ PyModule_AddIntConstant(
+ module, "SERVICE_PAUSED", SERVICE_PAUSED);
+ PyModule_AddIntConstant(
+ module, "SERVICE_RUNNING", SERVICE_RUNNING);
+ PyModule_AddIntConstant(
+ module, "SERVICE_START_PENDING", SERVICE_START_PENDING);
+ PyModule_AddIntConstant(
+ module, "SERVICE_STOP_PENDING", SERVICE_STOP_PENDING);
+ PyModule_AddIntConstant(
+ module, "SERVICE_STOPPED", SERVICE_STOPPED);
+ */
+
+ // ...for internal use in _psutil_windows.py
+ PyModule_AddIntConstant(
+ module, "INFINITE", INFINITE);
+ PyModule_AddIntConstant(
+ module, "ERROR_ACCESS_DENIED", ERROR_ACCESS_DENIED);
+ PyModule_AddIntConstant(
+ module, "ERROR_INVALID_NAME", ERROR_INVALID_NAME);
+ PyModule_AddIntConstant(
+ module, "ERROR_SERVICE_DOES_NOT_EXIST", ERROR_SERVICE_DOES_NOT_EXIST);
+ PyModule_AddIntConstant(
+ module, "ERROR_PRIVILEGE_NOT_HELD", ERROR_PRIVILEGE_NOT_HELD);
+ PyModule_AddIntConstant(
+ module, "WINVER", PSUTIL_WINVER);
+ PyModule_AddIntConstant(
+ module, "WINDOWS_VISTA", PSUTIL_WINDOWS_VISTA);
+ PyModule_AddIntConstant(
+ module, "WINDOWS_7", PSUTIL_WINDOWS_7);
+ PyModule_AddIntConstant(
+ module, "WINDOWS_8", PSUTIL_WINDOWS_8);
+ PyModule_AddIntConstant(
+ module, "WINDOWS_8_1", PSUTIL_WINDOWS_8_1);
+ PyModule_AddIntConstant(
+ module, "WINDOWS_10", PSUTIL_WINDOWS_10);
+
+#if PY_MAJOR_VERSION >= 3
+ return module;
+#endif
+}
diff --git a/contrib/python/psutil/py3/psutil/_pswindows.py b/contrib/python/psutil/py3/psutil/_pswindows.py
new file mode 100644
index 0000000000..98baef5955
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/_pswindows.py
@@ -0,0 +1,1105 @@
+# 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.
+
+"""Windows platform implementation."""
+
+import contextlib
+import errno
+import functools
+import os
+import signal
+import sys
+import time
+from collections import namedtuple
+
+from . import _common
+from ._common import AccessDenied
+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 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
+from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS
+from ._psutil_windows import HIGH_PRIORITY_CLASS
+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:
+ # We may get here if:
+ # 1) we are on an old Windows version
+ # 2) psutil was installed via pip + wheel
+ # See: https://github.com/giampaolo/psutil/issues/811
+ msg = "this Windows version is too old (< Windows Vista); "
+ msg += "psutil 3.4.2 is the latest version which supports Windows "
+ msg += "2000, XP and 2003 server"
+ raise RuntimeError(msg)
+ else:
+ raise
+
+if sys.version_info >= (3, 4):
+ import enum
+else:
+ enum = None
+
+# process priority constants, import from __init__.py:
+# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
+__extra__all__ = [
+ "win_service_iter", "win_service_get",
+ # Process priority
+ "ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS",
+ "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", "NORMAL_PRIORITY_CLASS",
+ "REALTIME_PRIORITY_CLASS",
+ # IO priority
+ "IOPRIO_VERYLOW", "IOPRIO_LOW", "IOPRIO_NORMAL", "IOPRIO_HIGH",
+ # others
+ "CONN_DELETE_TCB", "AF_LINK",
+]
+
+
+# =====================================================================
+# --- globals
+# =====================================================================
+
+CONN_DELETE_TCB = "DELETE_TCB"
+ERROR_PARTIAL_COPY = 299
+PYPY = '__pypy__' in sys.builtin_module_names
+
+if enum is None:
+ AF_LINK = -1
+else:
+ AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1})
+ AF_LINK = AddressFamily.AF_LINK
+
+TCP_STATUSES = {
+ cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED,
+ cext.MIB_TCP_STATE_SYN_SENT: _common.CONN_SYN_SENT,
+ cext.MIB_TCP_STATE_SYN_RCVD: _common.CONN_SYN_RECV,
+ cext.MIB_TCP_STATE_FIN_WAIT1: _common.CONN_FIN_WAIT1,
+ cext.MIB_TCP_STATE_FIN_WAIT2: _common.CONN_FIN_WAIT2,
+ cext.MIB_TCP_STATE_TIME_WAIT: _common.CONN_TIME_WAIT,
+ cext.MIB_TCP_STATE_CLOSED: _common.CONN_CLOSE,
+ cext.MIB_TCP_STATE_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
+ cext.MIB_TCP_STATE_LAST_ACK: _common.CONN_LAST_ACK,
+ cext.MIB_TCP_STATE_LISTEN: _common.CONN_LISTEN,
+ cext.MIB_TCP_STATE_CLOSING: _common.CONN_CLOSING,
+ cext.MIB_TCP_STATE_DELETE_TCB: CONN_DELETE_TCB,
+ cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
+}
+
+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
+ HIGH_PRIORITY_CLASS = HIGH_PRIORITY_CLASS
+ IDLE_PRIORITY_CLASS = IDLE_PRIORITY_CLASS
+ NORMAL_PRIORITY_CLASS = NORMAL_PRIORITY_CLASS
+ REALTIME_PRIORITY_CLASS = REALTIME_PRIORITY_CLASS
+
+ globals().update(Priority.__members__)
+
+if enum is None:
+ IOPRIO_VERYLOW = 0
+ IOPRIO_LOW = 1
+ 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(
+ num_handles=0,
+ ctx_switches=1,
+ user_time=2,
+ kernel_time=3,
+ create_time=4,
+ num_threads=5,
+ io_rcount=6,
+ io_wcount=7,
+ io_rbytes=8,
+ io_wbytes=9,
+ io_count_others=10,
+ io_bytes_others=11,
+ num_page_faults=12,
+ peak_wset=13,
+ wset=14,
+ peak_paged_pool=15,
+ paged_pool=16,
+ peak_non_paged_pool=17,
+ non_paged_pool=18,
+ pagefile=19,
+ peak_pagefile=20,
+ mem_private=21,
+)
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.cpu_times()
+scputimes = namedtuple('scputimes',
+ ['user', 'system', 'idle', 'interrupt', 'dpc'])
+# psutil.virtual_memory()
+svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
+# psutil.Process.memory_info()
+pmem = namedtuple(
+ 'pmem', ['rss', 'vms',
+ 'num_page_faults', 'peak_wset', 'wset', 'peak_paged_pool',
+ 'paged_pool', 'peak_nonpaged_pool', 'nonpaged_pool',
+ 'pagefile', 'peak_pagefile', 'private'])
+# psutil.Process.memory_full_info()
+pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
+# psutil.Process.memory_maps(grouped=True)
+pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss'])
+# psutil.Process.memory_maps(grouped=False)
+pmmap_ext = namedtuple(
+ 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
+# psutil.Process.io_counters()
+pio = namedtuple('pio', ['read_count', 'write_count',
+ 'read_bytes', 'write_bytes',
+ 'other_count', 'other_bytes'])
+
+
+# =====================================================================
+# --- utils
+# =====================================================================
+
+
+@lru_cache(maxsize=512)
+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"
+ """
+ rawdrive = '\\'.join(s.split('\\')[:3])
+ driveletter = cext.win32_QueryDosDevice(rawdrive)
+ remainder = s[len(rawdrive):]
+ return os.path.join(driveletter, remainder)
+
+
+def py2_strencode(s):
+ """Encode a unicode string to a byte string by using the default fs
+ encoding + "replace" error handler.
+ """
+ if PY3:
+ return s
+ else:
+ if isinstance(s, str):
+ return s
+ else:
+ return s.encode(ENCODING, ENCODING_ERRS)
+
+
+@memoize
+def getpagesize():
+ return cext.getpagesize()
+
+
+# =====================================================================
+# --- memory
+# =====================================================================
+
+
+def virtual_memory():
+ """System virtual memory as a namedtuple."""
+ mem = cext.virtual_mem()
+ totphys, availphys, totpagef, availpagef, totvirt, freevirt = mem
+ #
+ total = totphys
+ avail = availphys
+ free = availphys
+ used = total - avail
+ percent = usage_percent((total - avail), total, round_=1)
+ return svmem(total, avail, percent, used, free)
+
+
+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)
+ return _common.sswap(total, used, free, percent, 0, 0)
+
+
+# =====================================================================
+# --- disk
+# =====================================================================
+
+
+disk_io_counters = cext.disk_io_counters
+
+
+def disk_usage(path):
+ """Return disk usage associated with path."""
+ if PY3 and isinstance(path, bytes):
+ # XXX: do we want to use "strict"? Probably yes, in order
+ # to fail immediately. After all we are accepting input here...
+ path = path.decode(ENCODING, errors="strict")
+ total, free = cext.disk_usage(path)
+ used = total - free
+ percent = usage_percent(used, total, round_=1)
+ return _common.sdiskusage(total, used, free, percent)
+
+
+def disk_partitions(all):
+ """Return disk partitions."""
+ rawlist = cext.disk_partitions(all)
+ return [_common.sdiskpart(*x) for x in rawlist]
+
+
+# =====================================================================
+# --- CPU
+# =====================================================================
+
+
+def cpu_times():
+ """Return system CPU times as a named tuple."""
+ user, system, idle = cext.cpu_times()
+ # Internally, GetSystemTimes() is used, and it doesn't return
+ # 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)
+
+
+def per_cpu_times():
+ """Return system per-CPU times as a list of named tuples."""
+ ret = []
+ for user, system, idle, interrupt, dpc in cext.per_cpu_times():
+ item = scputimes(user, system, idle, interrupt, dpc)
+ ret.append(item)
+ return ret
+
+
+def cpu_count_logical():
+ """Return the number of logical CPUs in the system."""
+ 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_stats():
+ """Return CPU statistics."""
+ ctx_switches, interrupts, dpcs, syscalls = cext.cpu_stats()
+ soft_interrupts = 0
+ return _common.scpustats(ctx_switches, interrupts, soft_interrupts,
+ syscalls)
+
+
+def cpu_freq():
+ """Return CPU frequency.
+ On Windows per-cpu frequency is not supported.
+ """
+ curr, max_ = cext.cpu_freq()
+ min_ = 0.0
+ return [_common.scpufreq(float(curr), min_, float(max_))]
+
+
+_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"""
+ global _loadavg_inititialized
+
+ if not _loadavg_inititialized:
+ cext.init_loadavg_counter()
+ _loadavg_inititialized = True
+
+ # Drop to 2 decimal points which is what Linux does
+ raw_loads = cext.getloadavg()
+ return tuple([round(load, 2) for load in raw_loads])
+
+
+# =====================================================================
+# --- network
+# =====================================================================
+
+
+def net_connections(kind, _pid=-1):
+ """Return socket connections. If pid == -1 return system-wide
+ 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])))
+ 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)
+ ret.add(nt)
+ return list(ret)
+
+
+def net_if_stats():
+ """Get NIC stats (isup, duplex, speed, mtu)."""
+ ret = {}
+ rawdict = cext.net_if_stats()
+ for name, items in rawdict.items():
+ if not PY3:
+ assert isinstance(name, unicode), type(name)
+ name = py2_strencode(name)
+ isup, duplex, speed, mtu = items
+ if hasattr(_common, 'NicDuplex'):
+ duplex = _common.NicDuplex(duplex)
+ ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ return ret
+
+
+def net_io_counters():
+ """Return network I/O statistics for every network interface
+ installed on the system as a dict of raw tuples.
+ """
+ ret = cext.net_io_counters()
+ return dict([(py2_strencode(k), v) for k, v in ret.items()])
+
+
+def net_if_addrs():
+ """Return the addresses associated to each NIC."""
+ ret = []
+ for items in cext.net_if_addrs():
+ items = list(items)
+ items[0] = py2_strencode(items[0])
+ ret.append(items)
+ return ret
+
+
+# =====================================================================
+# --- sensors
+# =====================================================================
+
+
+def sensors_battery():
+ """Return battery information."""
+ # For constants meaning see:
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/
+ # aa373232(v=vs.85).aspx
+ acline_status, flags, percent, secsleft = cext.sensors_battery()
+ power_plugged = acline_status == 1
+ no_battery = bool(flags & 128)
+ charging = bool(flags & 8)
+
+ if no_battery:
+ return None
+ if power_plugged or charging:
+ secsleft = _common.POWER_TIME_UNLIMITED
+ elif secsleft == -1:
+ secsleft = _common.POWER_TIME_UNKNOWN
+
+ return _common.sbattery(percent, secsleft, power_plugged)
+
+
+# =====================================================================
+# --- other system functions
+# =====================================================================
+
+
+_last_btime = 0
+
+
+def boot_time():
+ """The system boot time expressed in seconds since the epoch."""
+ # This dirty hack is to adjust the precision of the returned
+ # value which may have a 1 second fluctuation, see:
+ # https://github.com/giampaolo/psutil/issues/1007
+ global _last_btime
+ ret = float(cext.boot_time())
+ if abs(ret - _last_btime) <= 1:
+ return _last_btime
+ else:
+ _last_btime = ret
+ return ret
+
+
+def users():
+ """Return currently connected users as a list of namedtuples."""
+ retlist = []
+ rawlist = cext.users()
+ for item in rawlist:
+ user, hostname, tstamp = item
+ user = py2_strencode(user)
+ nt = _common.suser(user, None, hostname, tstamp, None)
+ retlist.append(nt)
+ return retlist
+
+
+# =====================================================================
+# --- Windows services
+# =====================================================================
+
+
+def win_service_iter():
+ """Yields a list of WindowsService instances."""
+ for name, display_name in cext.winservice_enumerate():
+ yield WindowsService(py2_strencode(name), py2_strencode(display_name))
+
+
+def win_service_get(name):
+ """Open a Windows service and return it as a WindowsService instance."""
+ service = WindowsService(name, None)
+ service._display_name = service._query_config()['display_name']
+ return service
+
+
+class WindowsService(object):
+ """Represents an installed Windows service."""
+
+ def __init__(self, name, display_name):
+ self._name = name
+ self._display_name = display_name
+
+ def __str__(self):
+ details = "(name=%r, display_name=%r)" % (
+ self._name, self._display_name)
+ return "%s%s" % (self.__class__.__name__, details)
+
+ def __repr__(self):
+ return "<%s at %s>" % (self.__str__(), id(self))
+
+ def __eq__(self, other):
+ # Test for equality with another WindosService object based
+ # on name.
+ if not isinstance(other, WindowsService):
+ return NotImplemented
+ return self._name == other._name
+
+ def __ne__(self, other):
+ return not self == other
+
+ def _query_config(self):
+ with self._wrap_exceptions():
+ 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))
+
+ def _query_status(self):
+ with self._wrap_exceptions():
+ status, pid = cext.winservice_query_status(self._name)
+ if pid == 0:
+ pid = None
+ return dict(status=status, pid=pid)
+
+ @contextlib.contextmanager
+ def _wrap_exceptions(self):
+ """Ctx manager which translates bare OSError and WindowsError
+ exceptions into NoSuchProcess and AccessDenied.
+ """
+ try:
+ 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)
+ else:
+ raise
+
+ # config query
+
+ def name(self):
+ """The service name. This string is how a service is referenced
+ and can be passed to win_service_get() to get a new
+ WindowsService instance.
+ """
+ return self._name
+
+ def display_name(self):
+ """The service display name. The value is cached when this class
+ is instantiated.
+ """
+ return self._display_name
+
+ def binpath(self):
+ """The fully qualified path to the service binary/exe file as
+ a string, including command line arguments.
+ """
+ return self._query_config()['binpath']
+
+ def username(self):
+ """The name of the user that owns this service."""
+ return self._query_config()['username']
+
+ def start_type(self):
+ """A string which can either be "automatic", "manual" or
+ "disabled".
+ """
+ return self._query_config()['start_type']
+
+ # status query
+
+ def pid(self):
+ """The process PID, if any, else None. This can be passed
+ to Process class to control the service's process.
+ """
+ return self._query_status()['pid']
+
+ def status(self):
+ """Service status as a string."""
+ return self._query_status()['status']
+
+ def description(self):
+ """Service long description."""
+ return py2_strencode(cext.winservice_query_descr(self.name()))
+
+ # utils
+
+ def as_dict(self):
+ """Utility method retrieving all the information above as a
+ dictionary.
+ """
+ d = self._query_config()
+ d.update(self._query_status())
+ d['name'] = self.name()
+ d['display_name'] = self.display_name()
+ d['description'] = self.description()
+ return d
+
+ # actions
+ # XXX: the necessary C bindings for start() and stop() are
+ # implemented but for now I prefer not to expose them.
+ # I may change my mind in the future. Reasons:
+ # - they require Administrator privileges
+ # - can't implement a timeout for stop() (unless by using a thread,
+ # which sucks)
+ # - would require adding ServiceAlreadyStarted and
+ # ServiceAlreadyStopped exceptions, adding two new APIs.
+ # - we might also want to have modify(), which would basically mean
+ # rewriting win32serviceutil.ChangeServiceConfig, which involves a
+ # lot of stuff (and API constants which would pollute the API), see:
+ # http://pyxr.sourceforge.net/PyXR/c/python24/lib/site-packages/
+ # win32/lib/win32serviceutil.py.html#0175
+ # - psutil is typically about "read only" monitoring stuff;
+ # win_service_* APIs should only be used to retrieve a service and
+ # check whether it's running
+
+ # def start(self, timeout=None):
+ # with self._wrap_exceptions():
+ # cext.winservice_start(self.name())
+ # if timeout:
+ # giveup_at = time.time() + timeout
+ # while True:
+ # if self.status() == "running":
+ # return
+ # else:
+ # if time.time() > giveup_at:
+ # raise TimeoutExpired(timeout)
+ # else:
+ # time.sleep(.1)
+
+ # def stop(self):
+ # # Note: timeout is not implemented because it's just not
+ # # possible, see:
+ # # http://stackoverflow.com/questions/11973228/
+ # with self._wrap_exceptions():
+ # return cext.winservice_stop(self.name())
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+pids = cext.pids
+pid_exists = cext.pid_exists
+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
+ # 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)
+
+
+def convert_oserror(exc, pid=None, name=None):
+ """Convert OSError into NoSuchProcess or AccessDenied."""
+ assert isinstance(exc, OSError), exc
+ if is_permission_err(exc):
+ return AccessDenied(pid=pid, name=name)
+ if exc.errno == errno.ESRCH:
+ return NoSuchProcess(pid=pid, name=name)
+ raise exc
+
+
+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
+ """
+ @functools.wraps(fun)
+ def wrapper(self, *args, **kwargs):
+ delay = 0.0001
+ times = 33
+ for x in range(times): # retries for roughly 1 second
+ try:
+ return fun(self, *args, **kwargs)
+ except WindowsError as _:
+ err = _
+ if err.winerror == ERROR_PARTIAL_COPY:
+ 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)
+ return wrapper
+
+
+class Process(object):
+ """Wrapper class around underlying C implementation."""
+
+ __slots__ = ["pid", "_name", "_ppid", "_cache"]
+
+ def __init__(self, pid):
+ self.pid = pid
+ self._name = None
+ self._ppid = None
+
+ # --- oneshot() stuff
+
+ def oneshot_enter(self):
+ self._proc_info.cache_activate(self)
+ self.exe.cache_activate(self)
+
+ def oneshot_exit(self):
+ self._proc_info.cache_deactivate(self)
+ self.exe.cache_deactivate(self)
+
+ @memoize_when_activated
+ def _proc_info(self):
+ """Return multiple information about this process as a
+ raw tuple.
+ """
+ ret = cext.proc_info(self.pid)
+ assert len(ret) == len(pinfo_map)
+ return ret
+
+ def name(self):
+ """Return process name, which on Windows is always the final
+ part of the executable.
+ """
+ # This is how PIDs 0 and 4 are always represented in taskmgr
+ # and process-hacker.
+ if self.pid == 0:
+ return "System Idle Process"
+ if self.pid == 4:
+ return "System"
+ return os.path.basename(self.exe())
+
+ @wrap_exceptions
+ @memoize_when_activated
+ def exe(self):
+ if PYPY:
+ try:
+ exe = cext.proc_exe(self.pid)
+ except WindowsError as err:
+ # 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)
+ raise AccessDenied(self.pid, self._name)
+ raise
+ else:
+ exe = cext.proc_exe(self.pid)
+ if not PY3:
+ exe = py2_strencode(exe)
+ if exe.startswith('\\'):
+ return convert_dos_path(exe)
+ return exe # May be "Registry", "MemCompression", ...
+
+ @wrap_exceptions
+ @retry_error_partial_copy
+ def cmdline(self):
+ if cext.WINVER >= cext.WINDOWS_8_1:
+ # PEB method detects cmdline changes but requires more
+ # privileges: https://github.com/giampaolo/psutil/pull/1398
+ try:
+ ret = cext.proc_cmdline(self.pid, use_peb=True)
+ except OSError as err:
+ if is_permission_err(err):
+ ret = cext.proc_cmdline(self.pid, use_peb=False)
+ else:
+ raise
+ else:
+ ret = cext.proc_cmdline(self.pid, use_peb=True)
+ if PY3:
+ return ret
+ else:
+ return [py2_strencode(s) for s in ret]
+
+ @wrap_exceptions
+ @retry_error_partial_copy
+ def environ(self):
+ ustr = cext.proc_environ(self.pid)
+ if ustr and not PY3:
+ assert isinstance(ustr, unicode), type(ustr)
+ return parse_environ_block(py2_strencode(ustr))
+
+ def ppid(self):
+ try:
+ return ppid_map()[self.pid]
+ except KeyError:
+ raise NoSuchProcess(self.pid, self._name)
+
+ def _get_raw_meminfo(self):
+ try:
+ return cext.proc_memory_info(self.pid)
+ except OSError as err:
+ if is_permission_err(err):
+ # TODO: the C ext can probably be refactored in order
+ # to get this from cext.proc_info()
+ info = self._proc_info()
+ return (
+ info[pinfo_map['num_page_faults']],
+ info[pinfo_map['peak_wset']],
+ info[pinfo_map['wset']],
+ info[pinfo_map['peak_paged_pool']],
+ info[pinfo_map['paged_pool']],
+ info[pinfo_map['peak_non_paged_pool']],
+ info[pinfo_map['non_paged_pool']],
+ info[pinfo_map['pagefile']],
+ info[pinfo_map['peak_pagefile']],
+ info[pinfo_map['mem_private']],
+ )
+ raise
+
+ @wrap_exceptions
+ def memory_info(self):
+ # on Windows RSS == WorkingSetSize and VSM == PagefileUsage.
+ # Underlying C function returns fields of PROCESS_MEMORY_COUNTERS
+ # struct.
+ t = self._get_raw_meminfo()
+ rss = t[2] # wset
+ vms = t[7] # pagefile
+ 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, ))
+
+ def memory_maps(self):
+ try:
+ raw = cext.proc_memory_maps(self.pid)
+ except OSError as err:
+ # XXX - can't use wrap_exceptions decorator as we're
+ # returning a generator; probably needs refactoring.
+ raise convert_oserror(err, self.pid, self._name)
+ else:
+ for addr, perm, path, rss in raw:
+ path = convert_dos_path(path)
+ if not PY3:
+ path = py2_strencode(path)
+ addr = hex(addr)
+ yield (addr, perm, path, rss)
+
+ @wrap_exceptions
+ def kill(self):
+ return cext.proc_kill(self.pid)
+
+ @wrap_exceptions
+ def send_signal(self, sig):
+ 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())):
+ os.kill(self.pid, sig)
+ else:
+ raise ValueError(
+ "only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals "
+ "are supported on Windows")
+
+ @wrap_exceptions
+ def wait(self, timeout=None):
+ if timeout is None:
+ cext_timeout = cext.INFINITE
+ else:
+ # WaitForSingleObject() expects time in milliseconds.
+ cext_timeout = int(timeout * 1000)
+
+ timer = getattr(time, 'monotonic', time.time)
+ stop_at = timer() + timeout if timeout is not None else None
+
+ try:
+ # Exit code is supposed to come from GetExitCodeProcess().
+ # May also be None if OpenProcess() failed with
+ # ERROR_INVALID_PARAMETER, meaning PID is already gone.
+ exit_code = cext.proc_wait(self.pid, cext_timeout)
+ except cext.TimeoutExpired:
+ # WaitForSingleObject() returned WAIT_TIMEOUT. Just raise.
+ raise TimeoutExpired(timeout, self.pid, self._name)
+ except cext.TimeoutAbandoned:
+ # WaitForSingleObject() returned WAIT_ABANDONED, see:
+ # https://github.com/giampaolo/psutil/issues/1224
+ # We'll just rely on the internal polling and return None
+ # when the PID disappears. Subprocess module does the same
+ # (return None):
+ # https://github.com/python/cpython/blob/
+ # be50a7b627d0aa37e08fa8e2d5568891f19903ce/
+ # Lib/subprocess.py#L1193-L1194
+ exit_code = None
+
+ # At this point WaitForSingleObject() returned WAIT_OBJECT_0,
+ # meaning the process is gone. Stupidly there are cases where
+ # its PID may still stick around so we do a further internal
+ # polling.
+ delay = 0.0001
+ while True:
+ if not pid_exists(self.pid):
+ return exit_code
+ if stop_at and timer() >= stop_at:
+ raise TimeoutExpired(timeout, pid=self.pid, name=self._name)
+ time.sleep(delay)
+ delay = min(delay * 2, 0.04) # incremental delay
+
+ @wrap_exceptions
+ def username(self):
+ if self.pid in (0, 4):
+ return 'NT AUTHORITY\\SYSTEM'
+ domain, user = cext.proc_username(self.pid)
+ return py2_strencode(domain) + '\\' + py2_strencode(user)
+
+ @wrap_exceptions
+ def create_time(self):
+ # Note: proc_times() not put under oneshot() 'cause create_time()
+ # is already cached by the main Process class.
+ try:
+ user, system, created = cext.proc_times(self.pid)
+ return created
+ except OSError as err:
+ if is_permission_err(err):
+ return self._proc_info()[pinfo_map['create_time']]
+ raise
+
+ @wrap_exceptions
+ def num_threads(self):
+ return self._proc_info()[pinfo_map['num_threads']]
+
+ @wrap_exceptions
+ def threads(self):
+ rawlist = cext.proc_threads(self.pid)
+ retlist = []
+ for thread_id, utime, stime in rawlist:
+ ntuple = _common.pthread(thread_id, utime, stime)
+ retlist.append(ntuple)
+ return retlist
+
+ @wrap_exceptions
+ def cpu_times(self):
+ try:
+ user, system, created = cext.proc_times(self.pid)
+ except OSError as err:
+ if not is_permission_err(err):
+ raise
+ info = self._proc_info()
+ user = info[pinfo_map['user_time']]
+ system = info[pinfo_map['kernel_time']]
+ # Children user/system times are not retrievable (set to 0).
+ return _common.pcputimes(user, system, 0.0, 0.0)
+
+ @wrap_exceptions
+ def suspend(self):
+ cext.proc_suspend_or_resume(self.pid, True)
+
+ @wrap_exceptions
+ def resume(self):
+ cext.proc_suspend_or_resume(self.pid, False)
+
+ @wrap_exceptions
+ @retry_error_partial_copy
+ def cwd(self):
+ if self.pid in (0, 4):
+ raise AccessDenied(self.pid, self._name)
+ # return a normalized pathname since the native C function appends
+ # "\\" at the and of the path
+ path = cext.proc_cwd(self.pid)
+ return py2_strencode(os.path.normpath(path))
+
+ @wrap_exceptions
+ def open_files(self):
+ if self.pid in (0, 4):
+ return []
+ ret = set()
+ # Filenames come in in native format like:
+ # "\Device\HarddiskVolume1\Windows\systemew\file.txt"
+ # Convert the first part in the corresponding drive letter
+ # (e.g. "C:\") by using Windows's QueryDosDevice()
+ raw_file_names = cext.proc_open_files(self.pid)
+ for _file in raw_file_names:
+ _file = convert_dos_path(_file)
+ if isfile_strict(_file):
+ if not PY3:
+ _file = py2_strencode(_file)
+ ntuple = _common.popenfile(_file, -1)
+ ret.add(ntuple)
+ return list(ret)
+
+ @wrap_exceptions
+ def connections(self, kind='inet'):
+ return net_connections(kind, _pid=self.pid)
+
+ @wrap_exceptions
+ def nice_get(self):
+ value = cext.proc_priority_get(self.pid)
+ if enum is not None:
+ value = Priority(value)
+ return value
+
+ @wrap_exceptions
+ def nice_set(self, value):
+ return cext.proc_priority_set(self.pid, value)
+
+ @wrap_exceptions
+ def ionice_get(self):
+ ret = cext.proc_io_priority_get(self.pid)
+ if enum is not None:
+ ret = IOPriority(ret)
+ return ret
+
+ @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):
+ raise ValueError("%s is not a valid priority" % ioclass)
+ cext.proc_io_priority_set(self.pid, ioclass)
+
+ @wrap_exceptions
+ def io_counters(self):
+ try:
+ ret = cext.proc_io_counters(self.pid)
+ except OSError as err:
+ if not is_permission_err(err):
+ raise
+ info = self._proc_info()
+ ret = (
+ info[pinfo_map['io_rcount']],
+ info[pinfo_map['io_wcount']],
+ info[pinfo_map['io_rbytes']],
+ info[pinfo_map['io_wbytes']],
+ info[pinfo_map['io_count_others']],
+ info[pinfo_map['io_bytes_others']],
+ )
+ return pio(*ret)
+
+ @wrap_exceptions
+ def status(self):
+ suspended = cext.proc_is_suspended(self.pid)
+ if suspended:
+ return _common.STATUS_STOPPED
+ else:
+ return _common.STATUS_RUNNING
+
+ @wrap_exceptions
+ 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)
+
+ @wrap_exceptions
+ def cpu_affinity_set(self, value):
+ def to_bitmask(ls):
+ if not ls:
+ raise ValueError("invalid argument %r" % ls)
+ out = 0
+ for b in ls:
+ out |= 2 ** b
+ return out
+
+ # SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER
+ # is returned for an invalid CPU but this seems not to be true,
+ # therefore we check CPUs validy beforehand.
+ allcpus = list(range(len(per_cpu_times())))
+ for cpu in value:
+ if cpu not in allcpus:
+ if not isinstance(cpu, (int, long)):
+ raise TypeError(
+ "invalid CPU %r; an integer is required" % cpu)
+ else:
+ raise ValueError("invalid CPU %r" % cpu)
+
+ bitmask = to_bitmask(value)
+ cext.proc_cpu_affinity_set(self.pid, bitmask)
+
+ @wrap_exceptions
+ def num_handles(self):
+ try:
+ return cext.proc_num_handles(self.pid)
+ except OSError as err:
+ if is_permission_err(err):
+ return self._proc_info()[pinfo_map['num_handles']]
+ raise
+
+ @wrap_exceptions
+ def num_ctx_switches(self):
+ ctx_switches = self._proc_info()[pinfo_map['ctx_switches']]
+ # only voluntary ctx switches are supported
+ return _common.pctxsw(ctx_switches, 0)
diff --git a/contrib/python/psutil/py3/psutil/arch/aix/ifaddrs.h b/contrib/python/psutil/py3/psutil/arch/aix/ifaddrs.h
new file mode 100644
index 0000000000..e15802bf7b
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/aix/ifaddrs.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017, Arnon Yaari
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*! Based on code from
+ https://lists.samba.org/archive/samba-technical/2009-February/063079.html
+!*/
+
+
+#ifndef GENERIC_AIX_IFADDRS_H
+#define GENERIC_AIX_IFADDRS_H
+
+#include <sys/socket.h>
+#include <net/if.h>
+
+#undef ifa_dstaddr
+#undef ifa_broadaddr
+#define ifa_broadaddr ifa_dstaddr
+
+struct ifaddrs {
+ struct ifaddrs *ifa_next;
+ char *ifa_name;
+ unsigned int ifa_flags;
+ struct sockaddr *ifa_addr;
+ struct sockaddr *ifa_netmask;
+ struct sockaddr *ifa_dstaddr;
+};
+
+extern int getifaddrs(struct ifaddrs **);
+extern void freeifaddrs(struct ifaddrs *);
+#endif
diff --git a/contrib/python/psutil/py3/psutil/arch/osx/process_info.c b/contrib/python/psutil/py3/psutil/arch/osx/process_info.c
new file mode 100644
index 0000000000..fb9f24ffac
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/process_info.c
@@ -0,0 +1,384 @@
+/*
+ * 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
new file mode 100644
index 0000000000..ffa6230f7c
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/process_info.h
@@ -0,0 +1,17 @@
+/*
+ * 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/ya.make b/contrib/python/psutil/py3/psutil/arch/osx/ya.make
new file mode 100644
index 0000000000..613c49f924
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/osx/ya.make
@@ -0,0 +1,9 @@
+PY23_NATIVE_LIBRARY()
+
+LICENSE(BSD-3-Clause)
+
+SRCS(
+ process_info.c
+)
+
+END()
diff --git a/contrib/python/psutil/py3/psutil/arch/solaris/v10/ifaddrs.h b/contrib/python/psutil/py3/psutil/arch/solaris/v10/ifaddrs.h
new file mode 100644
index 0000000000..0953a9b99a
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/solaris/v10/ifaddrs.h
@@ -0,0 +1,26 @@
+/* Reference: https://lists.samba.org/archive/samba-technical/2009-February/063079.html */
+
+
+#ifndef __IFADDRS_H__
+#define __IFADDRS_H__
+
+#include <sys/socket.h>
+#include <net/if.h>
+
+#undef ifa_dstaddr
+#undef ifa_broadaddr
+#define ifa_broadaddr ifa_dstaddr
+
+struct ifaddrs {
+ struct ifaddrs *ifa_next;
+ char *ifa_name;
+ unsigned int ifa_flags;
+ struct sockaddr *ifa_addr;
+ struct sockaddr *ifa_netmask;
+ struct sockaddr *ifa_dstaddr;
+};
+
+extern int getifaddrs(struct ifaddrs **);
+extern void freeifaddrs(struct ifaddrs *);
+
+#endif
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/cpu.c b/contrib/python/psutil/py3/psutil/arch/windows/cpu.c
new file mode 100644
index 0000000000..18f32e5983
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/cpu.c
@@ -0,0 +1,415 @@
+/*
+ * 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 <windows.h>
+#include <PowrProf.h>
+
+#include "../../_psutil_common.h"
+
+
+/*
+ * Return the number of logical, active CPUs. Return 0 if undetermined.
+ * See discussion at: https://bugs.python.org/issue33166#msg314631
+ */
+static 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;
+}
+
+
+/*
+ * Retrieves system CPU timing information as a (user, system, idle)
+ * tuple. On a multiprocessor system, the values returned are the
+ * sum of the designated times across all processors.
+ */
+PyObject *
+psutil_cpu_times(PyObject *self, PyObject *args) {
+ double idle, kernel, user, system;
+ FILETIME idle_time, kernel_time, user_time;
+
+ if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) {
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ }
+
+ idle = (double)((HI_T * idle_time.dwHighDateTime) + \
+ (LO_T * idle_time.dwLowDateTime));
+ user = (double)((HI_T * user_time.dwHighDateTime) + \
+ (LO_T * user_time.dwLowDateTime));
+ kernel = (double)((HI_T * kernel_time.dwHighDateTime) + \
+ (LO_T * kernel_time.dwLowDateTime));
+
+ // Kernel time includes idle time.
+ // We return only busy kernel time subtracting idle time from
+ // kernel time.
+ system = (kernel - idle);
+ return Py_BuildValue("(ddd)", user, system, idle);
+}
+
+
+/*
+ * Same as above but for all system CPUs.
+ */
+PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+ double idle, kernel, systemt, user, interrupt, dpc;
+ NTSTATUS status;
+ _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL;
+ UINT i;
+ unsigned int ncpus;
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ // retrieves number of processors
+ ncpus = psutil_get_num_cpus(1);
+ if (ncpus == 0)
+ goto error;
+
+ // allocates an array of _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
+ // structures, one per processor
+ sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \
+ malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
+ if (sppi == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ // gets cpu time informations
+ status = NtQuerySystemInformation(
+ SystemProcessorPerformanceInformation,
+ sppi,
+ ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
+ NULL);
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status,
+ "NtQuerySystemInformation(SystemProcessorPerformanceInformation)"
+ );
+ goto error;
+ }
+
+ // computes system global times summing each
+ // processor value
+ idle = user = kernel = interrupt = dpc = 0;
+ for (i = 0; i < ncpus; i++) {
+ py_tuple = NULL;
+ user = (double)((HI_T * sppi[i].UserTime.HighPart) +
+ (LO_T * sppi[i].UserTime.LowPart));
+ idle = (double)((HI_T * sppi[i].IdleTime.HighPart) +
+ (LO_T * sppi[i].IdleTime.LowPart));
+ kernel = (double)((HI_T * sppi[i].KernelTime.HighPart) +
+ (LO_T * sppi[i].KernelTime.LowPart));
+ interrupt = (double)((HI_T * sppi[i].InterruptTime.HighPart) +
+ (LO_T * sppi[i].InterruptTime.LowPart));
+ dpc = (double)((HI_T * sppi[i].DpcTime.HighPart) +
+ (LO_T * sppi[i].DpcTime.LowPart));
+
+ // kernel time includes idle time on windows
+ // we return only busy kernel time subtracting
+ // idle time from kernel time
+ systemt = kernel - idle;
+ py_tuple = Py_BuildValue(
+ "(ddddd)",
+ user,
+ systemt,
+ idle,
+ interrupt,
+ dpc
+ );
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ }
+
+ free(sppi);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (sppi)
+ free(sppi);
+ return NULL;
+}
+
+
+/*
+ * Return the number of active, logical CPUs.
+ */
+PyObject *
+psutil_cpu_count_logical(PyObject *self, PyObject *args) {
+ unsigned int ncpus;
+
+ ncpus = psutil_get_num_cpus(0);
+ if (ncpus != 0)
+ return Py_BuildValue("I", ncpus);
+ else
+ Py_RETURN_NONE; // mimick os.cpu_count()
+}
+
+
+/*
+ * Return the number of physical CPU cores (hyper-thread CPUs count
+ * is excluded).
+ */
+PyObject *
+psutil_cpu_count_phys(PyObject *self, PyObject *args) {
+ DWORD rc;
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL;
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL;
+ DWORD length = 0;
+ DWORD offset = 0;
+ DWORD ncpus = 0;
+ DWORD prev_processor_info_size = 0;
+
+ // GetLogicalProcessorInformationEx() is available from Windows 7
+ // onward. Differently from GetLogicalProcessorInformation()
+ // it supports process groups, meaning this is able to report more
+ // than 64 CPUs. See:
+ // https://bugs.python.org/issue33166
+ if (GetLogicalProcessorInformationEx == NULL) {
+ psutil_debug("Win < 7; cpu_count_phys() forced to None");
+ Py_RETURN_NONE;
+ }
+
+ while (1) {
+ rc = GetLogicalProcessorInformationEx(
+ RelationAll, buffer, &length);
+ if (rc == FALSE) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ if (buffer) {
+ free(buffer);
+ }
+ buffer = \
+ (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)malloc(length);
+ if (NULL == buffer) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ }
+ else {
+ psutil_debug("GetLogicalProcessorInformationEx() returned ",
+ GetLastError());
+ goto return_none;
+ }
+ }
+ else {
+ break;
+ }
+ }
+
+ ptr = buffer;
+ while (offset < length) {
+ // Advance ptr by the size of the previous
+ // SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct.
+ ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) \
+ (((char*)ptr) + prev_processor_info_size);
+
+ if (ptr->Relationship == RelationProcessorCore) {
+ ncpus += 1;
+ }
+
+ // When offset == length, we've reached the last processor
+ // info struct in the buffer.
+ offset += ptr->Size;
+ prev_processor_info_size = ptr->Size;
+ }
+
+ free(buffer);
+ if (ncpus != 0) {
+ return Py_BuildValue("I", ncpus);
+ }
+ else {
+ psutil_debug("GetLogicalProcessorInformationEx() count was 0");
+ Py_RETURN_NONE; // mimick os.cpu_count()
+ }
+
+return_none:
+ if (buffer != NULL)
+ free(buffer);
+ Py_RETURN_NONE;
+}
+
+
+/*
+ * Return CPU statistics.
+ */
+PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+ NTSTATUS status;
+ _SYSTEM_PERFORMANCE_INFORMATION *spi = NULL;
+ _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL;
+ _SYSTEM_INTERRUPT_INFORMATION *InterruptInformation = NULL;
+ unsigned int ncpus;
+ UINT i;
+ ULONG64 dpcs = 0;
+ ULONG interrupts = 0;
+
+ // retrieves number of processors
+ ncpus = psutil_get_num_cpus(1);
+ if (ncpus == 0)
+ goto error;
+
+ // get syscalls / ctx switches
+ spi = (_SYSTEM_PERFORMANCE_INFORMATION *) \
+ malloc(ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION));
+ if (spi == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ status = NtQuerySystemInformation(
+ SystemPerformanceInformation,
+ spi,
+ ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION),
+ NULL);
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQuerySystemInformation(SystemPerformanceInformation)");
+ goto error;
+ }
+
+ // get DPCs
+ InterruptInformation = \
+ malloc(sizeof(_SYSTEM_INTERRUPT_INFORMATION) * ncpus);
+ if (InterruptInformation == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ status = NtQuerySystemInformation(
+ SystemInterruptInformation,
+ InterruptInformation,
+ ncpus * sizeof(SYSTEM_INTERRUPT_INFORMATION),
+ NULL);
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQuerySystemInformation(SystemInterruptInformation)");
+ goto error;
+ }
+ for (i = 0; i < ncpus; i++) {
+ dpcs += InterruptInformation[i].DpcCount;
+ }
+
+ // get interrupts
+ sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \
+ malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION));
+ if (sppi == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ status = NtQuerySystemInformation(
+ SystemProcessorPerformanceInformation,
+ sppi,
+ ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION),
+ NULL);
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status,
+ "NtQuerySystemInformation(SystemProcessorPerformanceInformation)");
+ goto error;
+ }
+
+ for (i = 0; i < ncpus; i++) {
+ interrupts += sppi[i].InterruptCount;
+ }
+
+ // done
+ free(spi);
+ free(InterruptInformation);
+ free(sppi);
+ return Py_BuildValue(
+ "kkkk",
+ spi->ContextSwitches,
+ interrupts,
+ (unsigned long)dpcs,
+ spi->SystemCalls
+ );
+
+error:
+ if (spi)
+ free(spi);
+ if (InterruptInformation)
+ free(InterruptInformation);
+ if (sppi)
+ free(sppi);
+ return NULL;
+}
+
+
+/*
+ * Return CPU frequency.
+ */
+PyObject *
+psutil_cpu_freq(PyObject *self, PyObject *args) {
+ PROCESSOR_POWER_INFORMATION *ppi;
+ NTSTATUS ret;
+ ULONG size;
+ LPBYTE pBuffer = NULL;
+ ULONG current;
+ ULONG max;
+ unsigned int ncpus;
+
+ // Get the number of CPUs.
+ ncpus = psutil_get_num_cpus(1);
+ if (ncpus == 0)
+ return NULL;
+
+ // Allocate size.
+ size = ncpus * sizeof(PROCESSOR_POWER_INFORMATION);
+ pBuffer = (BYTE*)LocalAlloc(LPTR, size);
+ if (! pBuffer) {
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ }
+
+ // Syscall.
+ ret = CallNtPowerInformation(
+ ProcessorInformation, NULL, 0, pBuffer, size);
+ if (ret != 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "CallNtPowerInformation syscall failed");
+ goto error;
+ }
+
+ // Results.
+ ppi = (PROCESSOR_POWER_INFORMATION *)pBuffer;
+ max = ppi->MaxMhz;
+ current = ppi->CurrentMhz;
+ LocalFree(pBuffer);
+
+ return Py_BuildValue("kk", current, max);
+
+error:
+ if (pBuffer != NULL)
+ LocalFree(pBuffer);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/cpu.h b/contrib/python/psutil/py3/psutil/arch/windows/cpu.h
new file mode 100644
index 0000000000..d88c221210
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/cpu.h
@@ -0,0 +1,14 @@
+/*
+ * 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_cpu_count_logical(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_count_phys(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/windows/disk.c b/contrib/python/psutil/py3/psutil/arch/windows/disk.c
new file mode 100644
index 0000000000..2288f22c56
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/disk.c
@@ -0,0 +1,388 @@
+/*
+ * 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 <windows.h>
+#include <tchar.h>
+#include <winioctl.h>
+
+#include "../../_psutil_common.h"
+
+
+#ifndef _ARRAYSIZE
+#define _ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
+#endif
+
+static char *psutil_get_drive_type(int type) {
+ switch (type) {
+ case DRIVE_FIXED:
+ return "fixed";
+ case DRIVE_CDROM:
+ return "cdrom";
+ case DRIVE_REMOVABLE:
+ return "removable";
+ case DRIVE_UNKNOWN:
+ return "unknown";
+ case DRIVE_NO_ROOT_DIR:
+ return "unmounted";
+ case DRIVE_REMOTE:
+ return "remote";
+ case DRIVE_RAMDISK:
+ return "ramdisk";
+ default:
+ return "?";
+ }
+}
+
+
+/*
+ * Return path's disk total and free as a Python tuple.
+ */
+PyObject *
+psutil_disk_usage(PyObject *self, PyObject *args) {
+ BOOL retval;
+ ULARGE_INTEGER _, total, free;
+ char *path;
+
+ if (PyArg_ParseTuple(args, "u", &path)) {
+ Py_BEGIN_ALLOW_THREADS
+ retval = GetDiskFreeSpaceExW((LPCWSTR)path, &_, &total, &free);
+ Py_END_ALLOW_THREADS
+ goto return_;
+ }
+
+ // 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
+ retval = GetDiskFreeSpaceEx(path, &_, &total, &free);
+ 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);
+}
+
+
+/*
+ * Return a Python dict of tuples for disk I/O information. This may
+ * require running "diskperf -y" command first.
+ */
+PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+ DISK_PERFORMANCE diskPerformance;
+ DWORD dwSize;
+ HANDLE hDevice = NULL;
+ char szDevice[MAX_PATH];
+ char szDeviceDisplay[MAX_PATH];
+ int devNum;
+ int i;
+ DWORD ioctrlSize;
+ BOOL ret;
+ PyObject *py_retdict = PyDict_New();
+ PyObject *py_tuple = NULL;
+
+ if (py_retdict == NULL)
+ return NULL;
+ // Apparently there's no way to figure out how many times we have
+ // to iterate in order to find valid drives.
+ // Let's assume 32, which is higher than 26, the number of letters
+ // in the alphabet (from A:\ to Z:\).
+ for (devNum = 0; devNum <= 32; ++devNum) {
+ py_tuple = NULL;
+ sprintf_s(szDevice, MAX_PATH, "\\\\.\\PhysicalDrive%d", devNum);
+ hDevice = CreateFile(szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL);
+ if (hDevice == INVALID_HANDLE_VALUE)
+ continue;
+
+ // DeviceIoControl() sucks!
+ i = 0;
+ ioctrlSize = sizeof(diskPerformance);
+ while (1) {
+ i += 1;
+ ret = DeviceIoControl(
+ hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, &diskPerformance,
+ ioctrlSize, &dwSize, NULL);
+ if (ret != 0)
+ break; // OK!
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ // Retry with a bigger buffer (+ limit for retries).
+ if (i <= 1024) {
+ ioctrlSize *= 2;
+ continue;
+ }
+ }
+ else if (GetLastError() == ERROR_INVALID_FUNCTION) {
+ // This happens on AppVeyor:
+ // https://ci.appveyor.com/project/giampaolo/psutil/build/
+ // 1364/job/ascpdi271b06jle3
+ // Assume it means we're dealing with some exotic disk
+ // and go on.
+ psutil_debug("DeviceIoControl -> ERROR_INVALID_FUNCTION; "
+ "ignore PhysicalDrive%i", devNum);
+ goto next;
+ }
+ else if (GetLastError() == ERROR_NOT_SUPPORTED) {
+ // Again, let's assume we're dealing with some exotic disk.
+ psutil_debug("DeviceIoControl -> ERROR_NOT_SUPPORTED; "
+ "ignore PhysicalDrive%i", devNum);
+ goto next;
+ }
+ // XXX: it seems we should also catch ERROR_INVALID_PARAMETER:
+ // https://sites.ualberta.ca/dept/aict/uts/software/openbsd/
+ // ports/4.1/i386/openafs/w-openafs-1.4.14-transarc/
+ // openafs-1.4.14/src/usd/usd_nt.c
+
+ // XXX: we can also bump into ERROR_MORE_DATA in which case
+ // (quoting doc) we're supposed to retry with a bigger buffer
+ // and specify a new "starting point", whatever it means.
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
+
+ sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%i", devNum);
+ py_tuple = Py_BuildValue(
+ "(IILLKK)",
+ diskPerformance.ReadCount,
+ diskPerformance.WriteCount,
+ diskPerformance.BytesRead,
+ diskPerformance.BytesWritten,
+ // convert to ms:
+ // https://github.com/giampaolo/psutil/issues/1012
+ (unsigned long long)
+ (diskPerformance.ReadTime.QuadPart) / 10000000,
+ (unsigned long long)
+ (diskPerformance.WriteTime.QuadPart) / 10000000);
+ if (!py_tuple)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, szDeviceDisplay, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+
+next:
+ CloseHandle(hDevice);
+ }
+
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retdict);
+ if (hDevice != NULL)
+ CloseHandle(hDevice);
+ return NULL;
+}
+
+
+/*
+ * Return disk partitions as a list of tuples such as
+ * (drive_letter, drive_letter, type, "")
+ */
+PyObject *
+psutil_disk_partitions(PyObject *self, PyObject *args) {
+ DWORD num_bytes;
+ char drive_strings[255];
+ char *drive_letter = drive_strings;
+ char mp_buf[MAX_PATH];
+ char mp_path[MAX_PATH];
+ int all;
+ int type;
+ int ret;
+ unsigned int old_mode = 0;
+ char opts[20];
+ HANDLE mp_h;
+ BOOL mp_flag= TRUE;
+ LPTSTR fs_type[MAX_PATH + 1] = { 0 };
+ DWORD pflags = 0;
+ DWORD lpMaximumComponentLength = 0; // max file name
+ PyObject *py_all;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+
+ if (py_retlist == NULL) {
+ return NULL;
+ }
+
+ // avoid to visualize a message box in case something goes wrong
+ // see https://github.com/giampaolo/psutil/issues/264
+ old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ if (! PyArg_ParseTuple(args, "O", &py_all))
+ goto error;
+ all = PyObject_IsTrue(py_all);
+
+ Py_BEGIN_ALLOW_THREADS
+ num_bytes = GetLogicalDriveStrings(254, drive_letter);
+ Py_END_ALLOW_THREADS
+
+ if (num_bytes == 0) {
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
+
+ while (*drive_letter != 0) {
+ py_tuple = NULL;
+ opts[0] = 0;
+ fs_type[0] = 0;
+
+ Py_BEGIN_ALLOW_THREADS
+ type = GetDriveType(drive_letter);
+ Py_END_ALLOW_THREADS
+
+ // by default we only show hard drives and cd-roms
+ if (all == 0) {
+ if ((type == DRIVE_UNKNOWN) ||
+ (type == DRIVE_NO_ROOT_DIR) ||
+ (type == DRIVE_REMOTE) ||
+ (type == DRIVE_RAMDISK)) {
+ goto next;
+ }
+ // floppy disk: skip it by default as it introduces a
+ // considerable slowdown.
+ if ((type == DRIVE_REMOVABLE) &&
+ (strcmp(drive_letter, "A:\\") == 0)) {
+ goto next;
+ }
+ }
+
+ ret = GetVolumeInformation(
+ (LPCTSTR)drive_letter,
+ NULL,
+ _ARRAYSIZE(drive_letter),
+ NULL,
+ &lpMaximumComponentLength,
+ &pflags,
+ (LPTSTR)fs_type,
+ _ARRAYSIZE(fs_type));
+ if (ret == 0) {
+ // We might get here in case of a floppy hard drive, in
+ // which case the error is (21, "device not ready").
+ // Let's pretend it didn't happen as we already have
+ // the drive name and type ('removable').
+ strcat_s(opts, _countof(opts), "");
+ SetLastError(0);
+ }
+ else {
+ if (pflags & FILE_READ_ONLY_VOLUME)
+ strcat_s(opts, _countof(opts), "ro");
+ else
+ strcat_s(opts, _countof(opts), "rw");
+ if (pflags & FILE_VOLUME_IS_COMPRESSED)
+ strcat_s(opts, _countof(opts), ",compressed");
+ if (pflags & FILE_READ_ONLY_VOLUME)
+ strcat_s(opts, _countof(opts), ",readonly");
+
+ // Check for mount points on this volume and add/get info
+ // (checks first to know if we can even have mount points)
+ if (pflags & FILE_SUPPORTS_REPARSE_POINTS) {
+ mp_h = FindFirstVolumeMountPoint(
+ drive_letter, mp_buf, MAX_PATH);
+ if (mp_h != INVALID_HANDLE_VALUE) {
+ while (mp_flag) {
+ // Append full mount path with drive letter
+ strcpy_s(mp_path, _countof(mp_path), drive_letter);
+ strcat_s(mp_path, _countof(mp_path), mp_buf);
+
+ py_tuple = Py_BuildValue(
+ "(ssssIi)",
+ drive_letter,
+ mp_path,
+ fs_type, // typically "NTFS"
+ opts,
+ lpMaximumComponentLength, // max file length
+ MAX_PATH // max path length
+ );
+
+ if (!py_tuple ||
+ PyList_Append(py_retlist, py_tuple) == -1) {
+ FindVolumeMountPointClose(mp_h);
+ goto error;
+ }
+
+ Py_CLEAR(py_tuple);
+
+ // Continue looking for more mount points
+ mp_flag = FindNextVolumeMountPoint(
+ mp_h, mp_buf, MAX_PATH);
+ }
+ FindVolumeMountPointClose(mp_h);
+ }
+
+ }
+ }
+
+ if (strlen(opts) > 0)
+ strcat_s(opts, _countof(opts), ",");
+ strcat_s(opts, _countof(opts), psutil_get_drive_type(type));
+
+ py_tuple = Py_BuildValue(
+ "(ssssIi)",
+ drive_letter,
+ drive_letter,
+ fs_type, // either FAT, FAT32, NTFS, HPFS, CDFS, UDF or NWFS
+ opts,
+ lpMaximumComponentLength, // max file length
+ MAX_PATH // max path length
+ );
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ goto next;
+
+next:
+ drive_letter = strchr(drive_letter, 0) + 1;
+ }
+
+ SetErrorMode(old_mode);
+ return py_retlist;
+
+error:
+ SetErrorMode(old_mode);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+
+
+/*
+ Accept a filename's drive in native format like "\Device\HarddiskVolume1\"
+ and return the corresponding drive letter (e.g. "C:\\").
+ If no match is found return an empty string.
+*/
+PyObject *
+psutil_win32_QueryDosDevice(PyObject *self, PyObject *args) {
+ LPCTSTR lpDevicePath;
+ TCHAR d = TEXT('A');
+ TCHAR szBuff[5];
+
+ if (!PyArg_ParseTuple(args, "s", &lpDevicePath))
+ return NULL;
+
+ while (d <= TEXT('Z')) {
+ TCHAR szDeviceName[3] = {d, TEXT(':'), TEXT('\0')};
+ TCHAR szTarget[512] = {0};
+ if (QueryDosDevice(szDeviceName, szTarget, 511) != 0) {
+ if (_tcscmp(lpDevicePath, szTarget) == 0) {
+ _stprintf_s(szBuff, _countof(szBuff), TEXT("%c:"), d);
+ return Py_BuildValue("s", szBuff);
+ }
+ }
+ d++;
+ }
+ return Py_BuildValue("s", "");
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/disk.h b/contrib/python/psutil/py3/psutil/arch/windows/disk.h
new file mode 100644
index 0000000000..298fb6ba0e
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/disk.h
@@ -0,0 +1,12 @@
+/*
+ * 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(PyObject *self, PyObject *args);
+PyObject *psutil_win32_QueryDosDevice(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/net.c b/contrib/python/psutil/py3/psutil/arch/windows/net.c
new file mode 100644
index 0000000000..8d8f7d1c0a
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/net.c
@@ -0,0 +1,437 @@
+/*
+ * 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.
+ */
+
+// Fixes clash between winsock2.h and windows.h
+#define WIN32_LEAN_AND_MEAN
+
+#include <Python.h>
+#include <windows.h>
+#include <wchar.h>
+#include <ws2tcpip.h>
+
+#include "../../_psutil_common.h"
+
+
+static PIP_ADAPTER_ADDRESSES
+psutil_get_nic_addresses(void) {
+ ULONG bufferLength = 0;
+ PIP_ADAPTER_ADDRESSES buffer;
+
+ if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &bufferLength)
+ != ERROR_BUFFER_OVERFLOW)
+ {
+ PyErr_SetString(PyExc_RuntimeError,
+ "GetAdaptersAddresses() syscall failed.");
+ return NULL;
+ }
+
+ buffer = malloc(bufferLength);
+ if (buffer == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ memset(buffer, 0, bufferLength);
+
+ if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, buffer, &bufferLength)
+ != ERROR_SUCCESS)
+ {
+ free(buffer);
+ PyErr_SetString(PyExc_RuntimeError,
+ "GetAdaptersAddresses() syscall failed.");
+ return NULL;
+ }
+
+ return buffer;
+}
+
+
+/*
+ * Return a Python list of named tuples with overall network I/O information
+ */
+PyObject *
+psutil_net_io_counters(PyObject *self, PyObject *args) {
+ DWORD dwRetVal = 0;
+ MIB_IF_ROW2 *pIfRow = NULL;
+ PIP_ADAPTER_ADDRESSES pAddresses = NULL;
+ PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
+ PyObject *py_retdict = PyDict_New();
+ PyObject *py_nic_info = NULL;
+ PyObject *py_nic_name = NULL;
+
+ if (py_retdict == NULL)
+ return NULL;
+ pAddresses = psutil_get_nic_addresses();
+ if (pAddresses == NULL)
+ goto error;
+ pCurrAddresses = pAddresses;
+
+ while (pCurrAddresses) {
+ py_nic_name = NULL;
+ py_nic_info = NULL;
+
+ pIfRow = (MIB_IF_ROW2 *) malloc(sizeof(MIB_IF_ROW2));
+ if (pIfRow == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ SecureZeroMemory((PVOID)pIfRow, sizeof(MIB_IF_ROW2));
+ pIfRow->InterfaceIndex = pCurrAddresses->IfIndex;
+ dwRetVal = GetIfEntry2(pIfRow);
+ if (dwRetVal != NO_ERROR) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "GetIfEntry() or GetIfEntry2() syscalls failed.");
+ goto error;
+ }
+
+ py_nic_info = Py_BuildValue(
+ "(KKKKKKKK)",
+ pIfRow->OutOctets,
+ pIfRow->InOctets,
+ (pIfRow->OutUcastPkts + pIfRow->OutNUcastPkts),
+ (pIfRow->InUcastPkts + pIfRow->InNUcastPkts),
+ pIfRow->InErrors,
+ pIfRow->OutErrors,
+ pIfRow->InDiscards,
+ pIfRow->OutDiscards);
+ if (!py_nic_info)
+ goto error;
+
+ py_nic_name = PyUnicode_FromWideChar(
+ pCurrAddresses->FriendlyName,
+ wcslen(pCurrAddresses->FriendlyName));
+
+ if (py_nic_name == NULL)
+ goto error;
+ if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info))
+ goto error;
+ Py_CLEAR(py_nic_name);
+ Py_CLEAR(py_nic_info);
+
+ free(pIfRow);
+ pCurrAddresses = pCurrAddresses->Next;
+ }
+
+ free(pAddresses);
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_nic_name);
+ Py_XDECREF(py_nic_info);
+ Py_DECREF(py_retdict);
+ if (pAddresses != NULL)
+ free(pAddresses);
+ if (pIfRow != NULL)
+ free(pIfRow);
+ return NULL;
+}
+
+
+/*
+ * Return NICs addresses.
+ */
+PyObject *
+psutil_net_if_addrs(PyObject *self, PyObject *args) {
+ unsigned int i = 0;
+ ULONG family;
+ PCTSTR intRet;
+ PCTSTR netmaskIntRet;
+ char *ptr;
+ char buff_addr[1024];
+ char buff_macaddr[1024];
+ char buff_netmask[1024];
+ DWORD dwRetVal = 0;
+ ULONG converted_netmask;
+ UINT netmask_bits;
+ struct in_addr in_netmask;
+ PIP_ADAPTER_ADDRESSES pAddresses = NULL;
+ PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
+ PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
+
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_address = NULL;
+ PyObject *py_mac_address = NULL;
+ PyObject *py_nic_name = NULL;
+ PyObject *py_netmask = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ pAddresses = psutil_get_nic_addresses();
+ if (pAddresses == NULL)
+ goto error;
+ pCurrAddresses = pAddresses;
+
+ while (pCurrAddresses) {
+ pUnicast = pCurrAddresses->FirstUnicastAddress;
+
+ netmaskIntRet = NULL;
+ py_nic_name = NULL;
+ py_nic_name = PyUnicode_FromWideChar(
+ pCurrAddresses->FriendlyName,
+ wcslen(pCurrAddresses->FriendlyName));
+ if (py_nic_name == NULL)
+ goto error;
+
+ // MAC address
+ if (pCurrAddresses->PhysicalAddressLength != 0) {
+ ptr = buff_macaddr;
+ *ptr = '\0';
+ for (i = 0; i < (int) pCurrAddresses->PhysicalAddressLength; i++) {
+ if (i == (pCurrAddresses->PhysicalAddressLength - 1)) {
+ sprintf_s(ptr, _countof(buff_macaddr), "%.2X\n",
+ (int)pCurrAddresses->PhysicalAddress[i]);
+ }
+ else {
+ sprintf_s(ptr, _countof(buff_macaddr), "%.2X-",
+ (int)pCurrAddresses->PhysicalAddress[i]);
+ }
+ ptr += 3;
+ }
+ *--ptr = '\0';
+
+ py_mac_address = Py_BuildValue("s", buff_macaddr);
+ if (py_mac_address == NULL)
+ goto error;
+
+ Py_INCREF(Py_None);
+ Py_INCREF(Py_None);
+ Py_INCREF(Py_None);
+ py_tuple = Py_BuildValue(
+ "(OiOOOO)",
+ py_nic_name,
+ -1, // this will be converted later to AF_LINK
+ py_mac_address,
+ Py_None, // netmask (not supported)
+ Py_None, // broadcast (not supported)
+ Py_None // ptp (not supported on Windows)
+ );
+ if (! py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ Py_CLEAR(py_mac_address);
+ }
+
+ // find out the IP address associated with the NIC
+ if (pUnicast != NULL) {
+ for (i = 0; pUnicast != NULL; i++) {
+ family = pUnicast->Address.lpSockaddr->sa_family;
+ if (family == AF_INET) {
+ struct sockaddr_in *sa_in = (struct sockaddr_in *)
+ pUnicast->Address.lpSockaddr;
+ intRet = inet_ntop(AF_INET, &(sa_in->sin_addr), buff_addr,
+ sizeof(buff_addr));
+ if (!intRet)
+ goto error;
+ netmask_bits = pUnicast->OnLinkPrefixLength;
+ dwRetVal = ConvertLengthToIpv4Mask(
+ netmask_bits, &converted_netmask);
+ if (dwRetVal == NO_ERROR) {
+ in_netmask.s_addr = converted_netmask;
+ netmaskIntRet = inet_ntop(
+ AF_INET, &in_netmask, buff_netmask,
+ sizeof(buff_netmask));
+ if (!netmaskIntRet)
+ goto error;
+ }
+ }
+ else if (family == AF_INET6) {
+ struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)
+ pUnicast->Address.lpSockaddr;
+ intRet = inet_ntop(AF_INET6, &(sa_in6->sin6_addr),
+ buff_addr, sizeof(buff_addr));
+ if (!intRet)
+ goto error;
+ }
+ else {
+ // we should never get here
+ pUnicast = pUnicast->Next;
+ continue;
+ }
+
+#if PY_MAJOR_VERSION >= 3
+ py_address = PyUnicode_FromString(buff_addr);
+#else
+ py_address = PyString_FromString(buff_addr);
+#endif
+ if (py_address == NULL)
+ goto error;
+
+ if (netmaskIntRet != NULL) {
+#if PY_MAJOR_VERSION >= 3
+ py_netmask = PyUnicode_FromString(buff_netmask);
+#else
+ py_netmask = PyString_FromString(buff_netmask);
+#endif
+ } else {
+ Py_INCREF(Py_None);
+ py_netmask = Py_None;
+ }
+
+ Py_INCREF(Py_None);
+ Py_INCREF(Py_None);
+ py_tuple = Py_BuildValue(
+ "(OiOOOO)",
+ py_nic_name,
+ family,
+ py_address,
+ py_netmask,
+ Py_None, // broadcast (not supported)
+ Py_None // ptp (not supported on Windows)
+ );
+
+ if (! py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ Py_CLEAR(py_address);
+ Py_CLEAR(py_netmask);
+
+ pUnicast = pUnicast->Next;
+ }
+ }
+ Py_CLEAR(py_nic_name);
+ pCurrAddresses = pCurrAddresses->Next;
+ }
+
+ free(pAddresses);
+ return py_retlist;
+
+error:
+ if (pAddresses)
+ free(pAddresses);
+ Py_DECREF(py_retlist);
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_address);
+ Py_XDECREF(py_nic_name);
+ Py_XDECREF(py_netmask);
+ return NULL;
+}
+
+
+/*
+ * Provides stats about NIC interfaces installed on the system.
+ * TODO: get 'duplex' (currently it's hard coded to '2', aka 'full duplex')
+ */
+PyObject *
+psutil_net_if_stats(PyObject *self, PyObject *args) {
+ int i;
+ DWORD dwSize = 0;
+ DWORD dwRetVal = 0;
+ MIB_IFTABLE *pIfTable;
+ MIB_IFROW *pIfRow;
+ PIP_ADAPTER_ADDRESSES pAddresses = NULL;
+ PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
+ char descr[MAX_PATH];
+ int ifname_found;
+
+ PyObject *py_nic_name = NULL;
+ PyObject *py_retdict = PyDict_New();
+ PyObject *py_ifc_info = NULL;
+ PyObject *py_is_up = NULL;
+
+ if (py_retdict == NULL)
+ return NULL;
+
+ pAddresses = psutil_get_nic_addresses();
+ if (pAddresses == NULL)
+ goto error;
+
+ pIfTable = (MIB_IFTABLE *) malloc(sizeof (MIB_IFTABLE));
+ if (pIfTable == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ dwSize = sizeof(MIB_IFTABLE);
+ if (GetIfTable(pIfTable, &dwSize, FALSE) == ERROR_INSUFFICIENT_BUFFER) {
+ free(pIfTable);
+ pIfTable = (MIB_IFTABLE *) malloc(dwSize);
+ if (pIfTable == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ }
+ // Make a second call to GetIfTable to get the actual
+ // data we want.
+ if ((dwRetVal = GetIfTable(pIfTable, &dwSize, FALSE)) != NO_ERROR) {
+ PyErr_SetString(PyExc_RuntimeError, "GetIfTable() syscall failed");
+ goto error;
+ }
+
+ for (i = 0; i < (int) pIfTable->dwNumEntries; i++) {
+ pIfRow = (MIB_IFROW *) & pIfTable->table[i];
+
+ // GetIfTable is not able to give us NIC with "friendly names"
+ // so we determine them via GetAdapterAddresses() which
+ // provides friendly names *and* descriptions and find the
+ // ones that match.
+ ifname_found = 0;
+ pCurrAddresses = pAddresses;
+ while (pCurrAddresses) {
+ sprintf_s(descr, MAX_PATH, "%wS", pCurrAddresses->Description);
+ if (lstrcmp(descr, pIfRow->bDescr) == 0) {
+ py_nic_name = PyUnicode_FromWideChar(
+ pCurrAddresses->FriendlyName,
+ wcslen(pCurrAddresses->FriendlyName));
+ if (py_nic_name == NULL)
+ goto error;
+ ifname_found = 1;
+ break;
+ }
+ pCurrAddresses = pCurrAddresses->Next;
+ }
+ if (ifname_found == 0) {
+ // Name not found means GetAdapterAddresses() doesn't list
+ // this NIC, only GetIfTable, meaning it's not really a NIC
+ // interface so we skip it.
+ continue;
+ }
+
+ // is up?
+ if ((pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_CONNECTED ||
+ pIfRow->dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL) &&
+ pIfRow->dwAdminStatus == 1 ) {
+ py_is_up = Py_True;
+ }
+ else {
+ py_is_up = Py_False;
+ }
+ Py_INCREF(py_is_up);
+
+ py_ifc_info = Py_BuildValue(
+ "(Oikk)",
+ py_is_up,
+ 2, // there's no way to know duplex so let's assume 'full'
+ pIfRow->dwSpeed / 1000000, // expressed in bytes, we want Mb
+ pIfRow->dwMtu
+ );
+ if (!py_ifc_info)
+ goto error;
+ if (PyDict_SetItem(py_retdict, py_nic_name, py_ifc_info))
+ goto error;
+ Py_CLEAR(py_nic_name);
+ Py_CLEAR(py_ifc_info);
+ }
+
+ free(pIfTable);
+ free(pAddresses);
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_is_up);
+ Py_XDECREF(py_ifc_info);
+ Py_XDECREF(py_nic_name);
+ Py_DECREF(py_retdict);
+ if (pIfTable != NULL)
+ free(pIfTable);
+ if (pAddresses != NULL)
+ free(pAddresses);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/net.h b/contrib/python/psutil/py3/psutil/arch/windows/net.h
new file mode 100644
index 0000000000..7a6158d13b
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/net.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_net_if_addrs(PyObject *self, PyObject *args);
+PyObject *psutil_net_if_stats(PyObject *self, PyObject *args);
+PyObject *psutil_net_io_counters(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
new file mode 100644
index 0000000000..a3cc932470
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/ntextapi.h
@@ -0,0 +1,711 @@
+/*
+ * 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.
+ * Define Windows structs and constants which are considered private.
+ */
+
+#if !defined(__NTEXTAPI_H__)
+#define __NTEXTAPI_H__
+#include <winternl.h>
+#include <iphlpapi.h>
+
+#ifndef PSUTIL_MAYBE_EXTERN
+#define PSUTIL_MAYBE_EXTERN extern
+#endif
+
+typedef LONG NTSTATUS;
+
+// https://github.com/ajkhoury/TestDll/blob/master/nt_ddk.h
+#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
+#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
+#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
+#define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L)
+#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
+
+// WtsApi32.h
+#define WTS_CURRENT_SERVER_HANDLE ((HANDLE)NULL)
+#define WINSTATIONNAME_LENGTH 32
+#define DOMAIN_LENGTH 17
+#define USERNAME_LENGTH 20
+
+// ================================================================
+// Enums
+// ================================================================
+
+#undef SystemExtendedHandleInformation
+#define SystemExtendedHandleInformation 64
+#undef MemoryWorkingSetInformation
+#define MemoryWorkingSetInformation 0x1
+#undef ObjectNameInformation
+#define ObjectNameInformation 1
+#undef ProcessIoPriority
+#define ProcessIoPriority 33
+#undef ProcessWow64Information
+#define ProcessWow64Information 26
+#undef SystemProcessIdInformation
+#define SystemProcessIdInformation 88
+
+// process suspend() / resume()
+typedef enum _KTHREAD_STATE {
+ Initialized,
+ Ready,
+ Running,
+ Standby,
+ Terminated,
+ Waiting,
+ Transition,
+ DeferredReady,
+ GateWait,
+ MaximumThreadState
+} KTHREAD_STATE, *PKTHREAD_STATE;
+
+typedef enum _KWAIT_REASON {
+ Executive,
+ FreePage,
+ PageIn,
+ PoolAllocation,
+ DelayExecution,
+ Suspended,
+ UserRequest,
+ WrExecutive,
+ WrFreePage,
+ WrPageIn,
+ WrPoolAllocation,
+ WrDelayExecution,
+ WrSuspended,
+ WrUserRequest,
+ WrEventPair,
+ WrQueue,
+ WrLpcReceive,
+ WrLpcReply,
+ WrVirtualMemory,
+ WrPageOut,
+ WrRendezvous,
+ WrKeyedEvent,
+ WrTerminated,
+ WrProcessInSwap,
+ WrCpuRateControl,
+ WrCalloutStack,
+ WrKernel,
+ WrResource,
+ WrPushLock,
+ WrMutex,
+ WrQuantumEnd,
+ WrDispatchInt,
+ WrPreempted,
+ WrYieldExecution,
+ WrFastMutex,
+ WrGuardedMutex,
+ WrRundown,
+ WrAlertByThreadId,
+ WrDeferredPreempt,
+ MaximumWaitReason
+} KWAIT_REASON, *PKWAIT_REASON;
+
+// users()
+typedef enum _WTS_INFO_CLASS {
+ WTSInitialProgram,
+ WTSApplicationName,
+ WTSWorkingDirectory,
+ WTSOEMId,
+ WTSSessionId,
+ WTSUserName,
+ WTSWinStationName,
+ WTSDomainName,
+ WTSConnectState,
+ WTSClientBuildNumber,
+ WTSClientName,
+ WTSClientDirectory,
+ WTSClientProductId,
+ WTSClientHardwareId,
+ WTSClientAddress,
+ WTSClientDisplay,
+ WTSClientProtocolType,
+ WTSIdleTime,
+ WTSLogonTime,
+ WTSIncomingBytes,
+ WTSOutgoingBytes,
+ WTSIncomingFrames,
+ WTSOutgoingFrames,
+ WTSClientInfo,
+ WTSSessionInfo,
+ WTSSessionInfoEx,
+ WTSConfigInfo,
+ WTSValidationInfo, // Info Class value used to fetch Validation Information through the WTSQuerySessionInformation
+ WTSSessionAddressV4,
+ WTSIsRemoteSession
+} WTS_INFO_CLASS;
+
+typedef enum _WTS_CONNECTSTATE_CLASS {
+ WTSActive, // User logged on to WinStation
+ WTSConnected, // WinStation connected to client
+ WTSConnectQuery, // In the process of connecting to client
+ WTSShadow, // Shadowing another WinStation
+ WTSDisconnected, // WinStation logged on without client
+ WTSIdle, // Waiting for client to connect
+ WTSListen, // WinStation is listening for connection
+ WTSReset, // WinStation is being reset
+ WTSDown, // WinStation is down due to error
+ WTSInit, // WinStation in initialization
+} WTS_CONNECTSTATE_CLASS;
+
+// ================================================================
+// Structs.
+// ================================================================
+
+// cpu_stats(), per_cpu_times()
+typedef struct {
+ LARGE_INTEGER IdleTime;
+ LARGE_INTEGER KernelTime;
+ LARGE_INTEGER UserTime;
+ LARGE_INTEGER DpcTime;
+ LARGE_INTEGER InterruptTime;
+ ULONG InterruptCount;
+} _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
+
+// cpu_stats()
+typedef struct {
+ LARGE_INTEGER IdleProcessTime;
+ LARGE_INTEGER IoReadTransferCount;
+ LARGE_INTEGER IoWriteTransferCount;
+ LARGE_INTEGER IoOtherTransferCount;
+ ULONG IoReadOperationCount;
+ ULONG IoWriteOperationCount;
+ ULONG IoOtherOperationCount;
+ ULONG AvailablePages;
+ ULONG CommittedPages;
+ ULONG CommitLimit;
+ ULONG PeakCommitment;
+ ULONG PageFaultCount;
+ ULONG CopyOnWriteCount;
+ ULONG TransitionCount;
+ ULONG CacheTransitionCount;
+ ULONG DemandZeroCount;
+ ULONG PageReadCount;
+ ULONG PageReadIoCount;
+ ULONG CacheReadCount;
+ ULONG CacheIoCount;
+ ULONG DirtyPagesWriteCount;
+ ULONG DirtyWriteIoCount;
+ ULONG MappedPagesWriteCount;
+ ULONG MappedWriteIoCount;
+ ULONG PagedPoolPages;
+ ULONG NonPagedPoolPages;
+ ULONG PagedPoolAllocs;
+ ULONG PagedPoolFrees;
+ ULONG NonPagedPoolAllocs;
+ ULONG NonPagedPoolFrees;
+ ULONG FreeSystemPtes;
+ ULONG ResidentSystemCodePage;
+ ULONG TotalSystemDriverPages;
+ ULONG TotalSystemCodePages;
+ ULONG NonPagedPoolLookasideHits;
+ ULONG PagedPoolLookasideHits;
+ ULONG AvailablePagedPoolPages;
+ ULONG ResidentSystemCachePage;
+ ULONG ResidentPagedPoolPage;
+ ULONG ResidentSystemDriverPage;
+ ULONG CcFastReadNoWait;
+ ULONG CcFastReadWait;
+ ULONG CcFastReadResourceMiss;
+ ULONG CcFastReadNotPossible;
+ ULONG CcFastMdlReadNoWait;
+ ULONG CcFastMdlReadWait;
+ ULONG CcFastMdlReadResourceMiss;
+ ULONG CcFastMdlReadNotPossible;
+ ULONG CcMapDataNoWait;
+ ULONG CcMapDataWait;
+ ULONG CcMapDataNoWaitMiss;
+ ULONG CcMapDataWaitMiss;
+ ULONG CcPinMappedDataCount;
+ ULONG CcPinReadNoWait;
+ ULONG CcPinReadWait;
+ ULONG CcPinReadNoWaitMiss;
+ ULONG CcPinReadWaitMiss;
+ ULONG CcCopyReadNoWait;
+ ULONG CcCopyReadWait;
+ ULONG CcCopyReadNoWaitMiss;
+ ULONG CcCopyReadWaitMiss;
+ ULONG CcMdlReadNoWait;
+ ULONG CcMdlReadWait;
+ ULONG CcMdlReadNoWaitMiss;
+ ULONG CcMdlReadWaitMiss;
+ ULONG CcReadAheadIos;
+ ULONG CcLazyWriteIos;
+ ULONG CcLazyWritePages;
+ ULONG CcDataFlushes;
+ ULONG CcDataPages;
+ ULONG ContextSwitches;
+ ULONG FirstLevelTbFills;
+ ULONG SecondLevelTbFills;
+ ULONG SystemCalls;
+} _SYSTEM_PERFORMANCE_INFORMATION;
+
+// cpu_stats()
+typedef struct {
+ ULONG ContextSwitches;
+ ULONG DpcCount;
+ ULONG DpcRate;
+ ULONG TimeIncrement;
+ ULONG DpcBypassCount;
+ ULONG ApcBypassCount;
+} _SYSTEM_INTERRUPT_INFORMATION;
+
+typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {
+ PVOID Object;
+ HANDLE UniqueProcessId;
+ HANDLE HandleValue;
+ ULONG GrantedAccess;
+ USHORT CreatorBackTraceIndex;
+ USHORT ObjectTypeIndex;
+ ULONG HandleAttributes;
+ ULONG Reserved;
+} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX;
+
+typedef struct _SYSTEM_HANDLE_INFORMATION_EX {
+ ULONG_PTR NumberOfHandles;
+ ULONG_PTR Reserved;
+ SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];
+} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
+
+typedef struct _CLIENT_ID2 {
+ HANDLE UniqueProcess;
+ HANDLE UniqueThread;
+} CLIENT_ID2, *PCLIENT_ID2;
+
+#define CLIENT_ID CLIENT_ID2
+#define PCLIENT_ID PCLIENT_ID2
+
+typedef struct _SYSTEM_THREAD_INFORMATION2 {
+ LARGE_INTEGER KernelTime;
+ LARGE_INTEGER UserTime;
+ LARGE_INTEGER CreateTime;
+ ULONG WaitTime;
+ PVOID StartAddress;
+ CLIENT_ID ClientId;
+ LONG Priority;
+ LONG BasePriority;
+ ULONG ContextSwitches;
+ ULONG ThreadState;
+ KWAIT_REASON WaitReason;
+} SYSTEM_THREAD_INFORMATION2, *PSYSTEM_THREAD_INFORMATION2;
+
+#define SYSTEM_THREAD_INFORMATION SYSTEM_THREAD_INFORMATION2
+#define PSYSTEM_THREAD_INFORMATION PSYSTEM_THREAD_INFORMATION2
+
+typedef struct _SYSTEM_PROCESS_INFORMATION2 {
+ ULONG NextEntryOffset;
+ ULONG NumberOfThreads;
+ LARGE_INTEGER SpareLi1;
+ LARGE_INTEGER SpareLi2;
+ LARGE_INTEGER SpareLi3;
+ LARGE_INTEGER CreateTime;
+ LARGE_INTEGER UserTime;
+ LARGE_INTEGER KernelTime;
+ UNICODE_STRING ImageName;
+ LONG BasePriority;
+ HANDLE UniqueProcessId;
+ HANDLE InheritedFromUniqueProcessId;
+ ULONG HandleCount;
+ ULONG SessionId;
+ ULONG_PTR PageDirectoryBase;
+ SIZE_T PeakVirtualSize;
+ SIZE_T VirtualSize;
+ DWORD PageFaultCount;
+ SIZE_T PeakWorkingSetSize;
+ SIZE_T WorkingSetSize;
+ SIZE_T QuotaPeakPagedPoolUsage;
+ SIZE_T QuotaPagedPoolUsage;
+ SIZE_T QuotaPeakNonPagedPoolUsage;
+ SIZE_T QuotaNonPagedPoolUsage;
+ SIZE_T PagefileUsage;
+ SIZE_T PeakPagefileUsage;
+ SIZE_T PrivatePageCount;
+ LARGE_INTEGER ReadOperationCount;
+ LARGE_INTEGER WriteOperationCount;
+ LARGE_INTEGER OtherOperationCount;
+ LARGE_INTEGER ReadTransferCount;
+ LARGE_INTEGER WriteTransferCount;
+ LARGE_INTEGER OtherTransferCount;
+ SYSTEM_THREAD_INFORMATION Threads[1];
+} SYSTEM_PROCESS_INFORMATION2, *PSYSTEM_PROCESS_INFORMATION2;
+
+#define SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION2
+#define PSYSTEM_PROCESS_INFORMATION PSYSTEM_PROCESS_INFORMATION2
+
+// cpu_freq()
+typedef struct _PROCESSOR_POWER_INFORMATION {
+ ULONG Number;
+ ULONG MaxMhz;
+ ULONG CurrentMhz;
+ ULONG MhzLimit;
+ ULONG MaxIdleState;
+ ULONG CurrentIdleState;
+} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
+
+#ifndef __IPHLPAPI_H__
+typedef struct in6_addr {
+ union {
+ UCHAR Byte[16];
+ USHORT Word[8];
+ } u;
+} IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR;
+#endif
+
+// PEB / cmdline(), cwd(), environ()
+typedef struct {
+ BYTE Reserved1[16];
+ PVOID Reserved2[5];
+ UNICODE_STRING CurrentDirectoryPath;
+ PVOID CurrentDirectoryHandle;
+ UNICODE_STRING DllPath;
+ UNICODE_STRING ImagePathName;
+ UNICODE_STRING CommandLine;
+ LPCWSTR env;
+} RTL_USER_PROCESS_PARAMETERS_, *PRTL_USER_PROCESS_PARAMETERS_;
+
+// users()
+typedef struct _WTS_SESSION_INFOW {
+ DWORD SessionId; // session id
+ LPWSTR pWinStationName; // name of WinStation this session is
+ // connected to
+ WTS_CONNECTSTATE_CLASS State; // connection state (see enum)
+} WTS_SESSION_INFOW, * PWTS_SESSION_INFOW;
+
+#define PWTS_SESSION_INFO PWTS_SESSION_INFOW
+
+typedef struct _WTS_CLIENT_ADDRESS {
+ DWORD AddressFamily; // AF_INET, AF_INET6, AF_IPX, AF_NETBIOS, AF_UNSPEC
+ BYTE Address[20]; // client network address
+} WTS_CLIENT_ADDRESS, * PWTS_CLIENT_ADDRESS;
+
+typedef struct _WTSINFOW {
+ WTS_CONNECTSTATE_CLASS State; // connection state (see enum)
+ DWORD SessionId; // session id
+ DWORD IncomingBytes;
+ DWORD OutgoingBytes;
+ DWORD IncomingFrames;
+ DWORD OutgoingFrames;
+ DWORD IncomingCompressedBytes;
+ DWORD OutgoingCompressedBytes;
+ WCHAR WinStationName[WINSTATIONNAME_LENGTH];
+ WCHAR Domain[DOMAIN_LENGTH];
+ WCHAR UserName[USERNAME_LENGTH + 1];// name of WinStation this session is
+ // connected to
+ LARGE_INTEGER ConnectTime;
+ LARGE_INTEGER DisconnectTime;
+ LARGE_INTEGER LastInputTime;
+ LARGE_INTEGER LogonTime;
+ LARGE_INTEGER CurrentTime;
+
+} WTSINFOW, * PWTSINFOW;
+
+#define PWTSINFO PWTSINFOW
+
+// cpu_count_phys()
+#if (_WIN32_WINNT < 0x0601) // Windows < 7 (Vista and XP)
+typedef struct _SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX {
+ LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
+ DWORD Size;
+ _ANONYMOUS_UNION
+ union {
+ PROCESSOR_RELATIONSHIP Processor;
+ NUMA_NODE_RELATIONSHIP NumaNode;
+ CACHE_RELATIONSHIP Cache;
+ GROUP_RELATIONSHIP Group;
+ } DUMMYUNIONNAME;
+} SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, \
+ *PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
+#endif
+
+// memory_uss()
+typedef struct _MEMORY_WORKING_SET_BLOCK {
+ ULONG_PTR Protection : 5;
+ ULONG_PTR ShareCount : 3;
+ ULONG_PTR Shared : 1;
+ ULONG_PTR Node : 3;
+#ifdef _WIN64
+ ULONG_PTR VirtualPage : 52;
+#else
+ ULONG VirtualPage : 20;
+#endif
+} MEMORY_WORKING_SET_BLOCK, *PMEMORY_WORKING_SET_BLOCK;
+
+// memory_uss()
+typedef struct _MEMORY_WORKING_SET_INFORMATION {
+ ULONG_PTR NumberOfEntries;
+ MEMORY_WORKING_SET_BLOCK WorkingSetInfo[1];
+} MEMORY_WORKING_SET_INFORMATION, *PMEMORY_WORKING_SET_INFORMATION;
+
+// memory_uss()
+typedef struct _PSUTIL_PROCESS_WS_COUNTERS {
+ SIZE_T NumberOfPages;
+ SIZE_T NumberOfPrivatePages;
+ SIZE_T NumberOfSharedPages;
+ SIZE_T NumberOfShareablePages;
+} PSUTIL_PROCESS_WS_COUNTERS, *PPSUTIL_PROCESS_WS_COUNTERS;
+
+// exe()
+typedef struct _SYSTEM_PROCESS_ID_INFORMATION {
+ HANDLE ProcessId;
+ UNICODE_STRING ImageName;
+} SYSTEM_PROCESS_ID_INFORMATION, *PSYSTEM_PROCESS_ID_INFORMATION;
+
+// ====================================================================
+// PEB structs for cmdline(), cwd(), environ()
+// ====================================================================
+
+#ifdef _WIN64
+typedef struct {
+ BYTE Reserved1[2];
+ BYTE BeingDebugged;
+ BYTE Reserved2[21];
+ PVOID LoaderData;
+ PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters;
+ // more fields...
+} PEB_;
+
+// When we are a 64 bit process accessing a 32 bit (WoW64)
+// process we need to use the 32 bit structure layout.
+typedef struct {
+ USHORT Length;
+ USHORT MaxLength;
+ DWORD Buffer;
+} UNICODE_STRING32;
+
+typedef struct {
+ BYTE Reserved1[16];
+ DWORD Reserved2[5];
+ UNICODE_STRING32 CurrentDirectoryPath;
+ DWORD CurrentDirectoryHandle;
+ UNICODE_STRING32 DllPath;
+ UNICODE_STRING32 ImagePathName;
+ UNICODE_STRING32 CommandLine;
+ DWORD env;
+} RTL_USER_PROCESS_PARAMETERS32;
+
+typedef struct {
+ BYTE Reserved1[2];
+ BYTE BeingDebugged;
+ BYTE Reserved2[1];
+ DWORD Reserved3[2];
+ DWORD Ldr;
+ DWORD ProcessParameters;
+ // more fields...
+} PEB32;
+#else // ! _WIN64
+typedef struct {
+ BYTE Reserved1[2];
+ BYTE BeingDebugged;
+ BYTE Reserved2[1];
+ PVOID Reserved3[2];
+ PVOID Ldr;
+ PRTL_USER_PROCESS_PARAMETERS_ ProcessParameters;
+ // more fields...
+} PEB_;
+
+// When we are a 32 bit (WoW64) process accessing a 64 bit process
+// we need to use the 64 bit structure layout and a special function
+// to read its memory.
+typedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)(
+ HANDLE ProcessHandle,
+ PVOID64 BaseAddress,
+ PVOID Buffer,
+ ULONG64 Size,
+ PULONG64 NumberOfBytesRead);
+
+typedef struct {
+ PVOID Reserved1[2];
+ PVOID64 PebBaseAddress;
+ PVOID Reserved2[4];
+ PVOID UniqueProcessId[2];
+ PVOID Reserved3[2];
+} PROCESS_BASIC_INFORMATION64;
+
+typedef struct {
+ USHORT Length;
+ USHORT MaxLength;
+ PVOID64 Buffer;
+} UNICODE_STRING64;
+
+typedef struct {
+ BYTE Reserved1[16];
+ PVOID64 Reserved2[5];
+ UNICODE_STRING64 CurrentDirectoryPath;
+ PVOID64 CurrentDirectoryHandle;
+ UNICODE_STRING64 DllPath;
+ UNICODE_STRING64 ImagePathName;
+ UNICODE_STRING64 CommandLine;
+ PVOID64 env;
+} RTL_USER_PROCESS_PARAMETERS64;
+
+typedef struct {
+ BYTE Reserved1[2];
+ BYTE BeingDebugged;
+ BYTE Reserved2[21];
+ PVOID64 LoaderData;
+ PVOID64 ProcessParameters;
+ // more fields...
+} PEB64;
+#endif // _WIN64
+
+// ================================================================
+// Type defs for modules loaded at runtime.
+// ================================================================
+
+PSUTIL_MAYBE_EXTERN BOOL (WINAPI *_GetLogicalProcessorInformationEx) (
+ LOGICAL_PROCESSOR_RELATIONSHIP relationship,
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Buffer,
+ PDWORD ReturnLength);
+
+#define GetLogicalProcessorInformationEx _GetLogicalProcessorInformationEx
+
+PSUTIL_MAYBE_EXTERN BOOLEAN (WINAPI * _WinStationQueryInformationW) (
+ HANDLE ServerHandle,
+ ULONG SessionId,
+ WINSTATIONINFOCLASS WinStationInformationClass,
+ PVOID pWinStationInformation,
+ ULONG WinStationInformationLength,
+ PULONG pReturnLength);
+
+#define WinStationQueryInformationW _WinStationQueryInformationW
+
+PSUTIL_MAYBE_EXTERN NTSTATUS (NTAPI *_NtQueryInformationProcess) (
+ HANDLE ProcessHandle,
+ DWORD ProcessInformationClass,
+ PVOID ProcessInformation,
+ DWORD ProcessInformationLength,
+ PDWORD ReturnLength);
+
+#define NtQueryInformationProcess _NtQueryInformationProcess
+
+PSUTIL_MAYBE_EXTERN NTSTATUS (NTAPI *_NtQuerySystemInformation) (
+ ULONG SystemInformationClass,
+ PVOID SystemInformation,
+ ULONG SystemInformationLength,
+ PULONG ReturnLength);
+
+#define NtQuerySystemInformation _NtQuerySystemInformation
+
+PSUTIL_MAYBE_EXTERN NTSTATUS (NTAPI *_NtSetInformationProcess) (
+ HANDLE ProcessHandle,
+ DWORD ProcessInformationClass,
+ PVOID ProcessInformation,
+ DWORD ProcessInformationLength);
+
+#define NtSetInformationProcess _NtSetInformationProcess
+
+PSUTIL_MAYBE_EXTERN PSTR (NTAPI * _RtlIpv4AddressToStringA) (
+ struct in_addr *Addr,
+ PSTR S);
+
+#define RtlIpv4AddressToStringA _RtlIpv4AddressToStringA
+
+PSUTIL_MAYBE_EXTERN PSTR (NTAPI * _RtlIpv6AddressToStringA) (
+ struct in6_addr *Addr,
+ PSTR P);
+
+#define RtlIpv6AddressToStringA _RtlIpv6AddressToStringA
+
+PSUTIL_MAYBE_EXTERN DWORD (WINAPI * _GetExtendedTcpTable) (
+ PVOID pTcpTable,
+ PDWORD pdwSize,
+ BOOL bOrder,
+ ULONG ulAf,
+ TCP_TABLE_CLASS TableClass,
+ ULONG Reserved);
+
+#define GetExtendedTcpTable _GetExtendedTcpTable
+
+PSUTIL_MAYBE_EXTERN DWORD (WINAPI * _GetExtendedUdpTable) (
+ PVOID pUdpTable,
+ PDWORD pdwSize,
+ BOOL bOrder,
+ ULONG ulAf,
+ UDP_TABLE_CLASS TableClass,
+ ULONG Reserved);
+
+#define GetExtendedUdpTable _GetExtendedUdpTable
+
+PSUTIL_MAYBE_EXTERN DWORD (CALLBACK *_GetActiveProcessorCount) (
+ WORD GroupNumber);
+
+#define GetActiveProcessorCount _GetActiveProcessorCount
+
+PSUTIL_MAYBE_EXTERN BOOL(CALLBACK *_WTSQuerySessionInformationW) (
+ HANDLE hServer,
+ DWORD SessionId,
+ WTS_INFO_CLASS WTSInfoClass,
+ LPWSTR* ppBuffer,
+ DWORD* pBytesReturned
+ );
+
+#define WTSQuerySessionInformationW _WTSQuerySessionInformationW
+
+PSUTIL_MAYBE_EXTERN BOOL(CALLBACK *_WTSEnumerateSessionsW)(
+ HANDLE hServer,
+ DWORD Reserved,
+ DWORD Version,
+ PWTS_SESSION_INFO* ppSessionInfo,
+ DWORD* pCount
+ );
+
+#define WTSEnumerateSessionsW _WTSEnumerateSessionsW
+
+PSUTIL_MAYBE_EXTERN VOID(CALLBACK *_WTSFreeMemory)(
+ IN PVOID pMemory
+ );
+
+#define WTSFreeMemory _WTSFreeMemory
+
+PSUTIL_MAYBE_EXTERN ULONGLONG (CALLBACK *_GetTickCount64) (
+ void);
+
+#define GetTickCount64 _GetTickCount64
+
+PSUTIL_MAYBE_EXTERN NTSTATUS (NTAPI *_NtQueryObject) (
+ HANDLE Handle,
+ OBJECT_INFORMATION_CLASS ObjectInformationClass,
+ PVOID ObjectInformation,
+ ULONG ObjectInformationLength,
+ PULONG ReturnLength);
+
+#define NtQueryObject _NtQueryObject
+
+PSUTIL_MAYBE_EXTERN NTSTATUS (WINAPI *_RtlGetVersion) (
+ PRTL_OSVERSIONINFOW lpVersionInformation
+);
+
+#define RtlGetVersion _RtlGetVersion
+
+PSUTIL_MAYBE_EXTERN NTSTATUS (WINAPI *_NtResumeProcess) (
+ HANDLE hProcess
+);
+
+#define NtResumeProcess _NtResumeProcess
+
+PSUTIL_MAYBE_EXTERN NTSTATUS (WINAPI *_NtSuspendProcess) (
+ HANDLE hProcess
+);
+
+#define NtSuspendProcess _NtSuspendProcess
+
+PSUTIL_MAYBE_EXTERN NTSTATUS (NTAPI *_NtQueryVirtualMemory) (
+ HANDLE ProcessHandle,
+ PVOID BaseAddress,
+ int MemoryInformationClass,
+ PVOID MemoryInformation,
+ SIZE_T MemoryInformationLength,
+ PSIZE_T ReturnLength
+);
+
+#define NtQueryVirtualMemory _NtQueryVirtualMemory
+
+PSUTIL_MAYBE_EXTERN ULONG (WINAPI *_RtlNtStatusToDosErrorNoTeb) (
+ NTSTATUS status
+);
+
+#define RtlNtStatusToDosErrorNoTeb _RtlNtStatusToDosErrorNoTeb
+
+#endif // __NTEXTAPI_H__
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_handles.c b/contrib/python/psutil/py3/psutil/arch/windows/process_handles.c
new file mode 100644
index 0000000000..72e3f4d41e
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/process_handles.c
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ */
+
+/*
+ * This module retrieves handles opened by a process.
+ * We use NtQuerySystemInformation to enumerate them and NtQueryObject
+ * to obtain the corresponding file name.
+ * Since NtQueryObject hangs for certain handle types we call it in a
+ * separate thread which gets killed if it doesn't complete within 100ms.
+ * This is a limitation of the Windows API and ProcessHacker uses the
+ * same trick: https://github.com/giampaolo/psutil/pull/597
+ *
+ * CREDITS: original implementation was written by Jeff Tang.
+ * It was then rewritten by Giampaolo Rodola many years later.
+ * Utility functions for getting the file handles and names were re-adapted
+ * from the excellent ProcessHacker.
+ */
+
+#include <windows.h>
+#include <Python.h>
+
+#include "../../_psutil_common.h"
+#include "process_utils.h"
+
+
+#define THREAD_TIMEOUT 100 // ms
+// Global object shared between the 2 threads.
+PUNICODE_STRING globalFileName = NULL;
+
+
+static int
+psutil_enum_handles(PSYSTEM_HANDLE_INFORMATION_EX *handles) {
+ static ULONG initialBufferSize = 0x10000;
+ NTSTATUS status;
+ PVOID buffer;
+ ULONG bufferSize;
+
+ bufferSize = initialBufferSize;
+ buffer = MALLOC_ZERO(bufferSize);
+ if (buffer == NULL) {
+ PyErr_NoMemory();
+ return 1;
+ }
+
+ while ((status = NtQuerySystemInformation(
+ SystemExtendedHandleInformation,
+ 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,
+ "SystemExtendedHandleInformation buffer too big");
+ return 1;
+ }
+
+ buffer = MALLOC_ZERO(bufferSize);
+ if (buffer == NULL) {
+ PyErr_NoMemory();
+ return 1;
+ }
+ }
+
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation");
+ FREE(buffer);
+ return 1;
+ }
+
+ *handles = (PSYSTEM_HANDLE_INFORMATION_EX)buffer;
+ return 0;
+}
+
+
+static int
+psutil_get_filename(LPVOID lpvParam) {
+ HANDLE hFile = *((HANDLE*)lpvParam);
+ NTSTATUS status;
+ ULONG bufferSize;
+ ULONG attempts = 8;
+
+ bufferSize = 0x200;
+ globalFileName = MALLOC_ZERO(bufferSize);
+ if (globalFileName == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+
+ // Note: also this is supposed to hang, hence why we do it in here.
+ if (GetFileType(hFile) != FILE_TYPE_DISK) {
+ SetLastError(0);
+ globalFileName->Length = 0;
+ return 0;
+ }
+
+ // A loop is needed because the I/O subsystem likes to give us the
+ // wrong return lengths...
+ do {
+ status = NtQueryObject(
+ hFile,
+ ObjectNameInformation,
+ globalFileName,
+ bufferSize,
+ &bufferSize
+ );
+ if (status == STATUS_BUFFER_OVERFLOW ||
+ status == STATUS_INFO_LENGTH_MISMATCH ||
+ status == STATUS_BUFFER_TOO_SMALL)
+ {
+ FREE(globalFileName);
+ globalFileName = MALLOC_ZERO(bufferSize);
+ if (globalFileName == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ }
+ else {
+ break;
+ }
+ } while (--attempts);
+
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(status, "NtQuerySystemInformation");
+ FREE(globalFileName);
+ globalFileName = NULL;
+ return 1;
+ }
+
+ return 0;
+
+error:
+ if (globalFileName != NULL) {
+ FREE(globalFileName);
+ globalFileName = NULL;
+ }
+ return 1;
+}
+
+
+static DWORD
+psutil_threaded_get_filename(HANDLE hFile) {
+ DWORD dwWait;
+ HANDLE hThread;
+ DWORD threadRetValue;
+
+ hThread = CreateThread(
+ NULL, 0, (LPTHREAD_START_ROUTINE)psutil_get_filename, &hFile, 0, NULL);
+ if (hThread == NULL) {
+ PyErr_SetFromOSErrnoWithSyscall("CreateThread");
+ return 1;
+ }
+
+ // Wait for the worker thread to finish.
+ dwWait = WaitForSingleObject(hThread, THREAD_TIMEOUT);
+
+ // If the thread hangs, kill it and cleanup.
+ if (dwWait == WAIT_TIMEOUT) {
+ psutil_debug(
+ "get handle name thread timed out after %i ms", THREAD_TIMEOUT);
+ if (TerminateThread(hThread, 0) == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("TerminateThread");
+ CloseHandle(hThread);
+ return 1;
+ }
+ CloseHandle(hThread);
+ return 0;
+ }
+
+ if (dwWait == WAIT_FAILED) {
+ psutil_debug("WaitForSingleObject -> WAIT_FAILED");
+ if (TerminateThread(hThread, 0) == 0) {
+ PyErr_SetFromOSErrnoWithSyscall(
+ "WaitForSingleObject -> WAIT_FAILED -> TerminateThread");
+ CloseHandle(hThread);
+ return 1;
+ }
+ PyErr_SetFromOSErrnoWithSyscall("WaitForSingleObject");
+ CloseHandle(hThread);
+ return 1;
+ }
+
+ if (GetExitCodeThread(hThread, &threadRetValue) == 0) {
+ if (TerminateThread(hThread, 0) == 0) {
+ PyErr_SetFromOSErrnoWithSyscall(
+ "GetExitCodeThread (failed) -> TerminateThread");
+ CloseHandle(hThread);
+ return 1;
+ }
+
+ CloseHandle(hThread);
+ PyErr_SetFromOSErrnoWithSyscall("GetExitCodeThread");
+ return 1;
+ }
+ CloseHandle(hThread);
+ return threadRetValue;
+}
+
+
+PyObject *
+psutil_get_open_files(DWORD dwPid, HANDLE hProcess) {
+ PSYSTEM_HANDLE_INFORMATION_EX handlesList = NULL;
+ PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL;
+ HANDLE hFile = NULL;
+ ULONG i = 0;
+ BOOLEAN errorOccurred = FALSE;
+ PyObject* py_path = NULL;
+ PyObject* py_retlist = PyList_New(0);;
+
+ if (!py_retlist)
+ return NULL;
+
+ // Due to the use of global variables, ensure only 1 call
+ // to psutil_get_open_files() is running.
+ EnterCriticalSection(&PSUTIL_CRITICAL_SECTION);
+
+ if (psutil_enum_handles(&handlesList) != 0)
+ goto error;
+
+ for (i = 0; i < handlesList->NumberOfHandles; i++) {
+ hHandle = &handlesList->Handles[i];
+ if ((ULONG_PTR)hHandle->UniqueProcessId != dwPid)
+ continue;
+ if (! DuplicateHandle(
+ hProcess,
+ hHandle->HandleValue,
+ GetCurrentProcess(),
+ &hFile,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ // Will fail if not a regular file; just skip it.
+ continue;
+ }
+
+ // This will set *globalFileName* global variable.
+ if (psutil_threaded_get_filename(hFile) != 0)
+ goto error;
+
+ if ((globalFileName != NULL) && (globalFileName->Length > 0)) {
+ py_path = PyUnicode_FromWideChar(globalFileName->Buffer,
+ wcslen(globalFileName->Buffer));
+ if (! py_path)
+ goto error;
+ if (PyList_Append(py_retlist, py_path))
+ goto error;
+ Py_CLEAR(py_path); // also sets to NULL
+ }
+
+ // Loop cleanup section.
+ if (globalFileName != NULL) {
+ FREE(globalFileName);
+ globalFileName = NULL;
+ }
+ CloseHandle(hFile);
+ hFile = NULL;
+ }
+
+ goto exit;
+
+error:
+ Py_XDECREF(py_retlist);
+ errorOccurred = TRUE;
+ goto exit;
+
+exit:
+ if (hFile != NULL)
+ CloseHandle(hFile);
+ if (globalFileName != NULL) {
+ FREE(globalFileName);
+ globalFileName = NULL;
+ }
+ if (py_path != NULL)
+ Py_DECREF(py_path);
+ if (handlesList != NULL)
+ FREE(handlesList);
+
+ LeaveCriticalSection(&PSUTIL_CRITICAL_SECTION);
+ if (errorOccurred == TRUE)
+ return NULL;
+ return py_retlist;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_handles.h b/contrib/python/psutil/py3/psutil/arch/windows/process_handles.h
new file mode 100644
index 0000000000..d1be3152d5
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/process_handles.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>
+#include <windows.h>
+
+PyObject* psutil_get_open_files(DWORD pid, HANDLE hProcess);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_info.c b/contrib/python/psutil/py3/psutil/arch/windows/process_info.c
new file mode 100644
index 0000000000..d44c4eb75e
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/process_info.c
@@ -0,0 +1,798 @@
+/*
+ * 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_windows module methods.
+ */
+
+#include <Python.h>
+#include <windows.h>
+
+#include "../../_psutil_common.h"
+#include "process_info.h"
+#include "process_utils.h"
+
+
+#ifndef _WIN64
+typedef NTSTATUS (NTAPI *__NtQueryInformationProcess)(
+ HANDLE ProcessHandle,
+ DWORD ProcessInformationClass,
+ PVOID ProcessInformation,
+ DWORD ProcessInformationLength,
+ PDWORD ReturnLength);
+#endif
+
+
+/*
+ * Given a pointer into a process's memory, figure out how much
+ * data can be read from it.
+ */
+static int
+psutil_get_process_region_size(HANDLE hProcess, LPCVOID src, SIZE_T *psize) {
+ MEMORY_BASIC_INFORMATION info;
+
+ if (!VirtualQueryEx(hProcess, src, &info, sizeof(info))) {
+ PyErr_SetFromOSErrnoWithSyscall("VirtualQueryEx");
+ return -1;
+ }
+
+ *psize = info.RegionSize - ((char*)src - (char*)info.BaseAddress);
+ return 0;
+}
+
+
+enum psutil_process_data_kind {
+ KIND_CMDLINE,
+ KIND_CWD,
+ KIND_ENVIRON,
+};
+
+
+static void
+psutil_convert_winerr(ULONG err, char* syscall) {
+ char fullmsg[8192];
+
+ if (err == ERROR_NOACCESS) {
+ sprintf(fullmsg, "%s -> ERROR_NOACCESS", syscall);
+ psutil_debug(fullmsg);
+ AccessDenied(fullmsg);
+ }
+ else {
+ PyErr_SetFromOSErrnoWithSyscall(syscall);
+ }
+}
+
+
+static void
+psutil_convert_ntstatus_err(NTSTATUS status, char* syscall) {
+ ULONG err;
+
+ if (NT_NTWIN32(status))
+ err = WIN32_FROM_NTSTATUS(status);
+ else
+ err = RtlNtStatusToDosErrorNoTeb(status);
+ psutil_convert_winerr(err, syscall);
+}
+
+
+static void
+psutil_giveup_with_ad(NTSTATUS status, char* syscall) {
+ ULONG err;
+ char fullmsg[8192];
+
+ if (NT_NTWIN32(status))
+ err = WIN32_FROM_NTSTATUS(status);
+ else
+ err = RtlNtStatusToDosErrorNoTeb(status);
+ sprintf(fullmsg, "%s -> %lu (%s)", syscall, err, strerror(err));
+ psutil_debug(fullmsg);
+ AccessDenied(fullmsg);
+}
+
+
+/*
+ * Get data from the process with the given pid. The data is returned
+ * in the pdata output member as a nul terminated string which must be
+ * freed on success.
+ * On success 0 is returned. On error the output parameter is not touched,
+ * -1 is returned, and an appropriate Python exception is set.
+ */
+static int
+psutil_get_process_data(DWORD pid,
+ enum psutil_process_data_kind kind,
+ WCHAR **pdata,
+ SIZE_T *psize) {
+ /* This function is quite complex because there are several cases to be
+ considered:
+
+ Two cases are really simple: we (i.e. the python interpreter) and the
+ target process are both 32 bit or both 64 bit. In that case the memory
+ layout of the structures matches up and all is well.
+
+ When we are 64 bit and the target process is 32 bit we need to use
+ custom 32 bit versions of the structures.
+
+ When we are 32 bit and the target process is 64 bit we need to use
+ custom 64 bit version of the structures. Also we need to use separate
+ Wow64 functions to get the information.
+
+ A few helper structs are defined above so that the compiler can handle
+ calculating the correct offsets.
+
+ Additional help also came from the following sources:
+
+ https://github.com/kohsuke/winp and
+ http://wj32.org/wp/2009/01/24/howto-get-the-command-line-of-processes/
+ http://stackoverflow.com/a/14012919
+ http://www.drdobbs.com/embracing-64-bit-windows/184401966
+ */
+ SIZE_T size = 0;
+ HANDLE hProcess = NULL;
+ LPCVOID src;
+ WCHAR *buffer = NULL;
+#ifdef _WIN64
+ LPVOID ppeb32 = NULL;
+#else
+ static __NtQueryInformationProcess NtWow64QueryInformationProcess64 = NULL;
+ static _NtWow64ReadVirtualMemory64 NtWow64ReadVirtualMemory64 = NULL;
+ PVOID64 src64;
+ BOOL weAreWow64;
+ BOOL theyAreWow64;
+#endif
+ DWORD access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
+ NTSTATUS status;
+
+ hProcess = psutil_handle_from_pid(pid, access);
+ if (hProcess == NULL)
+ return -1;
+
+#ifdef _WIN64
+ /* 64 bit case. Check if the target is a 32 bit process running in WoW64
+ * mode. */
+ status = NtQueryInformationProcess(
+ hProcess,
+ ProcessWow64Information,
+ &ppeb32,
+ sizeof(LPVOID),
+ NULL);
+
+ if (!NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQueryInformationProcess(ProcessWow64Information)");
+ goto error;
+ }
+
+ if (ppeb32 != NULL) {
+ /* We are 64 bit. Target process is 32 bit running in WoW64 mode. */
+ PEB32 peb32;
+ RTL_USER_PROCESS_PARAMETERS32 procParameters32;
+
+ // read PEB
+ if (!ReadProcessMemory(hProcess, ppeb32, &peb32, sizeof(peb32), NULL)) {
+ // May fail with ERROR_PARTIAL_COPY, see:
+ // https://github.com/giampaolo/psutil/issues/875
+ psutil_convert_winerr(GetLastError(), "ReadProcessMemory");
+ goto error;
+ }
+
+ // read process parameters
+ if (!ReadProcessMemory(hProcess,
+ UlongToPtr(peb32.ProcessParameters),
+ &procParameters32,
+ sizeof(procParameters32),
+ NULL))
+ {
+ // May fail with ERROR_PARTIAL_COPY, see:
+ // https://github.com/giampaolo/psutil/issues/875
+ psutil_convert_winerr(GetLastError(), "ReadProcessMemory");
+ goto error;
+ }
+
+ switch (kind) {
+ case KIND_CMDLINE:
+ src = UlongToPtr(procParameters32.CommandLine.Buffer),
+ size = procParameters32.CommandLine.Length;
+ break;
+ case KIND_CWD:
+ src = UlongToPtr(procParameters32.CurrentDirectoryPath.Buffer);
+ size = procParameters32.CurrentDirectoryPath.Length;
+ break;
+ case KIND_ENVIRON:
+ src = UlongToPtr(procParameters32.env);
+ break;
+ }
+ } else
+#else // #ifdef _WIN64
+ // 32 bit process. In here we may run into a lot of errors, e.g.:
+ // * [Error 0] The operation completed successfully
+ // (originated from NtWow64ReadVirtualMemory64)
+ // * [Error 998] Invalid access to memory location:
+ // (originated from NtWow64ReadVirtualMemory64)
+ // Refs:
+ // * https://github.com/giampaolo/psutil/issues/1839
+ // * https://github.com/giampaolo/psutil/pull/1866
+ // Since the following code is quite hackish and fails unpredictably,
+ // in case of any error from NtWow64* APIs we raise AccessDenied.
+
+ // 32 bit case. Check if the target is also 32 bit.
+ if (!IsWow64Process(GetCurrentProcess(), &weAreWow64) ||
+ !IsWow64Process(hProcess, &theyAreWow64)) {
+ PyErr_SetFromOSErrnoWithSyscall("IsWow64Process");
+ goto error;
+ }
+
+ if (weAreWow64 && !theyAreWow64) {
+ /* We are 32 bit running in WoW64 mode. Target process is 64 bit. */
+ PROCESS_BASIC_INFORMATION64 pbi64;
+ PEB64 peb64;
+ RTL_USER_PROCESS_PARAMETERS64 procParameters64;
+
+ if (NtWow64QueryInformationProcess64 == NULL) {
+ NtWow64QueryInformationProcess64 = \
+ psutil_GetProcAddressFromLib(
+ "ntdll.dll", "NtWow64QueryInformationProcess64");
+ if (NtWow64QueryInformationProcess64 == NULL) {
+ PyErr_Clear();
+ AccessDenied("can't query 64-bit process in 32-bit-WoW mode");
+ goto error;
+ }
+ }
+ if (NtWow64ReadVirtualMemory64 == NULL) {
+ NtWow64ReadVirtualMemory64 = \
+ psutil_GetProcAddressFromLib(
+ "ntdll.dll", "NtWow64ReadVirtualMemory64");
+ if (NtWow64ReadVirtualMemory64 == NULL) {
+ PyErr_Clear();
+ AccessDenied("can't query 64-bit process in 32-bit-WoW mode");
+ goto error;
+ }
+ }
+
+ status = NtWow64QueryInformationProcess64(
+ hProcess,
+ ProcessBasicInformation,
+ &pbi64,
+ sizeof(pbi64),
+ NULL);
+ if (!NT_SUCCESS(status)) {
+ /*
+ psutil_convert_ntstatus_err(
+ status,
+ "NtWow64QueryInformationProcess64(ProcessBasicInformation)");
+ */
+ psutil_giveup_with_ad(
+ status,
+ "NtWow64QueryInformationProcess64(ProcessBasicInformation)");
+ goto error;
+ }
+
+ // read peb
+ status = NtWow64ReadVirtualMemory64(
+ hProcess,
+ pbi64.PebBaseAddress,
+ &peb64,
+ sizeof(peb64),
+ NULL);
+ if (!NT_SUCCESS(status)) {
+ /*
+ psutil_convert_ntstatus_err(
+ status, "NtWow64ReadVirtualMemory64(pbi64.PebBaseAddress)");
+ */
+ psutil_giveup_with_ad(
+ status,
+ "NtWow64ReadVirtualMemory64(pbi64.PebBaseAddress)");
+ goto error;
+ }
+
+ // read process parameters
+ status = NtWow64ReadVirtualMemory64(
+ hProcess,
+ peb64.ProcessParameters,
+ &procParameters64,
+ sizeof(procParameters64),
+ NULL);
+ if (!NT_SUCCESS(status)) {
+ /*
+ psutil_convert_ntstatus_err(
+ status, "NtWow64ReadVirtualMemory64(peb64.ProcessParameters)");
+ */
+ psutil_giveup_with_ad(
+ status,
+ "NtWow64ReadVirtualMemory64(peb64.ProcessParameters)");
+ goto error;
+ }
+
+ switch (kind) {
+ case KIND_CMDLINE:
+ src64 = procParameters64.CommandLine.Buffer;
+ size = procParameters64.CommandLine.Length;
+ break;
+ case KIND_CWD:
+ src64 = procParameters64.CurrentDirectoryPath.Buffer,
+ size = procParameters64.CurrentDirectoryPath.Length;
+ break;
+ case KIND_ENVIRON:
+ src64 = procParameters64.env;
+ break;
+ }
+ } else
+#endif
+ /* Target process is of the same bitness as us. */
+ {
+ PROCESS_BASIC_INFORMATION pbi;
+ PEB_ peb;
+ RTL_USER_PROCESS_PARAMETERS_ procParameters;
+
+ status = NtQueryInformationProcess(
+ hProcess,
+ ProcessBasicInformation,
+ &pbi,
+ sizeof(pbi),
+ NULL);
+
+ if (!NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQueryInformationProcess(ProcessBasicInformation)");
+ goto error;
+ }
+
+
+ // read peb
+ if (!ReadProcessMemory(hProcess,
+ pbi.PebBaseAddress,
+ &peb,
+ sizeof(peb),
+ NULL))
+ {
+ // May fail with ERROR_PARTIAL_COPY, see:
+ // https://github.com/giampaolo/psutil/issues/875
+ psutil_convert_winerr(GetLastError(), "ReadProcessMemory");
+ goto error;
+ }
+
+ // read process parameters
+ if (!ReadProcessMemory(hProcess,
+ peb.ProcessParameters,
+ &procParameters,
+ sizeof(procParameters),
+ NULL))
+ {
+ // May fail with ERROR_PARTIAL_COPY, see:
+ // https://github.com/giampaolo/psutil/issues/875
+ psutil_convert_winerr(GetLastError(), "ReadProcessMemory");
+ goto error;
+ }
+
+ switch (kind) {
+ case KIND_CMDLINE:
+ src = procParameters.CommandLine.Buffer;
+ size = procParameters.CommandLine.Length;
+ break;
+ case KIND_CWD:
+ src = procParameters.CurrentDirectoryPath.Buffer;
+ size = procParameters.CurrentDirectoryPath.Length;
+ break;
+ case KIND_ENVIRON:
+ src = procParameters.env;
+ break;
+ }
+ }
+
+ if (kind == KIND_ENVIRON) {
+#ifndef _WIN64
+ if (weAreWow64 && !theyAreWow64) {
+ AccessDenied("can't query 64-bit process in 32-bit-WoW mode");
+ goto error;
+ }
+ else
+#endif
+ if (psutil_get_process_region_size(hProcess, src, &size) != 0)
+ goto error;
+ }
+
+ buffer = calloc(size + 2, 1);
+ if (buffer == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+#ifndef _WIN64
+ if (weAreWow64 && !theyAreWow64) {
+ status = NtWow64ReadVirtualMemory64(
+ hProcess,
+ src64,
+ buffer,
+ size,
+ NULL);
+ if (!NT_SUCCESS(status)) {
+ // psutil_convert_ntstatus_err(status, "NtWow64ReadVirtualMemory64");
+ psutil_giveup_with_ad(status, "NtWow64ReadVirtualMemory64");
+ goto error;
+ }
+ } else
+#endif
+ if (!ReadProcessMemory(hProcess, src, buffer, size, NULL)) {
+ // May fail with ERROR_PARTIAL_COPY, see:
+ // https://github.com/giampaolo/psutil/issues/875
+ psutil_convert_winerr(GetLastError(), "ReadProcessMemory");
+ goto error;
+ }
+
+ CloseHandle(hProcess);
+
+ *pdata = buffer;
+ *psize = size;
+
+ return 0;
+
+error:
+ if (hProcess != NULL)
+ CloseHandle(hProcess);
+ if (buffer != NULL)
+ free(buffer);
+ return -1;
+}
+
+
+/*
+ * Get process cmdline by using NtQueryInformationProcess. This is a
+ * method alternative to PEB which is less likely to result in
+ * AccessDenied. Requires Windows 8.1+.
+ */
+static int
+psutil_cmdline_query_proc(DWORD pid, WCHAR **pdata, SIZE_T *psize) {
+ HANDLE hProcess = NULL;
+ ULONG bufLen = 0;
+ NTSTATUS status;
+ char * buffer = NULL;
+ WCHAR * bufWchar = NULL;
+ PUNICODE_STRING tmp = NULL;
+ size_t size;
+ int ProcessCommandLineInformation = 60;
+
+ if (PSUTIL_WINVER < PSUTIL_WINDOWS_8_1) {
+ PyErr_SetString(
+ PyExc_RuntimeError, "requires Windows 8.1+");
+ goto error;
+ }
+
+ hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION);
+ if (hProcess == NULL)
+ goto error;
+
+ // get the right buf size
+ status = NtQueryInformationProcess(
+ hProcess,
+ ProcessCommandLineInformation,
+ NULL,
+ 0,
+ &bufLen);
+
+ // https://github.com/giampaolo/psutil/issues/1501
+ if (status == STATUS_NOT_FOUND) {
+ AccessDenied("NtQueryInformationProcess(ProcessBasicInformation) -> "
+ "STATUS_NOT_FOUND");
+ goto error;
+ }
+
+ if (status != STATUS_BUFFER_OVERFLOW && \
+ status != STATUS_BUFFER_TOO_SMALL && \
+ status != STATUS_INFO_LENGTH_MISMATCH) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQueryInformationProcess(ProcessBasicInformation)");
+ goto error;
+ }
+
+ // allocate memory
+ buffer = calloc(bufLen, 1);
+ if (buffer == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ // get the cmdline
+ status = NtQueryInformationProcess(
+ hProcess,
+ ProcessCommandLineInformation,
+ buffer,
+ bufLen,
+ &bufLen
+ );
+ if (!NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQueryInformationProcess(ProcessCommandLineInformation)");
+ goto error;
+ }
+
+ // build the string
+ tmp = (PUNICODE_STRING)buffer;
+ size = wcslen(tmp->Buffer) + 1;
+ bufWchar = (WCHAR *)calloc(size, sizeof(WCHAR));
+ if (bufWchar == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ wcscpy_s(bufWchar, size, tmp->Buffer);
+ *pdata = bufWchar;
+ *psize = size * sizeof(WCHAR);
+ free(buffer);
+ CloseHandle(hProcess);
+ return 0;
+
+error:
+ if (buffer != NULL)
+ free(buffer);
+ if (hProcess != NULL)
+ CloseHandle(hProcess);
+ return -1;
+}
+
+
+/*
+ * Return a Python list representing the arguments for the process
+ * with given pid or NULL on error.
+ */
+PyObject *
+psutil_get_cmdline(DWORD pid, int use_peb) {
+ PyObject *ret = NULL;
+ WCHAR *data = NULL;
+ SIZE_T size;
+ PyObject *py_retlist = NULL;
+ PyObject *py_unicode = NULL;
+ LPWSTR *szArglist = NULL;
+ int nArgs, i;
+ int func_ret;
+
+ /*
+ Reading the PEB to get the cmdline seem to be the best method if
+ somebody has tampered with the parameters after creating the process.
+ For instance, create a process as suspended, patch the command line
+ in its PEB and unfreeze it. It requires more privileges than
+ NtQueryInformationProcess though (the fallback):
+ - https://github.com/giampaolo/psutil/pull/1398
+ - https://blog.xpnsec.com/how-to-argue-like-cobalt-strike/
+ */
+ if (use_peb == 1)
+ func_ret = psutil_get_process_data(pid, KIND_CMDLINE, &data, &size);
+ else
+ func_ret = psutil_cmdline_query_proc(pid, &data, &size);
+ if (func_ret != 0)
+ goto out;
+
+ // attempt to parse the command line using Win32 API
+ szArglist = CommandLineToArgvW(data, &nArgs);
+ if (szArglist == NULL) {
+ PyErr_SetFromOSErrnoWithSyscall("CommandLineToArgvW");
+ goto out;
+ }
+
+ // 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;
+ 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);
+ py_unicode = NULL;
+ }
+ ret = py_retlist;
+ py_retlist = NULL;
+
+out:
+ if (szArglist != NULL)
+ LocalFree(szArglist);
+ if (data != NULL)
+ free(data);
+ Py_XDECREF(py_unicode);
+ Py_XDECREF(py_retlist);
+ return ret;
+}
+
+
+PyObject *
+psutil_get_cwd(DWORD pid) {
+ PyObject *ret = NULL;
+ WCHAR *data = NULL;
+ SIZE_T size;
+
+ if (psutil_get_process_data(pid, KIND_CWD, &data, &size) != 0)
+ goto out;
+
+ // convert wchar array to a Python unicode string
+ ret = PyUnicode_FromWideChar(data, wcslen(data));
+
+out:
+ if (data != NULL)
+ free(data);
+
+ return ret;
+}
+
+
+/*
+ * returns a Python string containing the environment variable data for the
+ * process with given pid or NULL on error.
+ */
+PyObject *
+psutil_get_environ(DWORD pid) {
+ PyObject *ret = NULL;
+ WCHAR *data = NULL;
+ SIZE_T size;
+
+ if (psutil_get_process_data(pid, KIND_ENVIRON, &data, &size) != 0)
+ goto out;
+
+ // convert wchar array to a Python unicode string
+ ret = PyUnicode_FromWideChar(data, size / 2);
+
+out:
+ if (data != NULL)
+ free(data);
+ return ret;
+}
+
+
+/*
+ * Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure
+ * fills the structure with various process information in one shot
+ * by using NtQuerySystemInformation.
+ * We use this as a fallback when faster functions fail with access
+ * denied. This is slower because it iterates over all processes
+ * but it doesn't require any privilege (also work for PID 0).
+ * On success return 1, else 0 with Python exception already set.
+ */
+int
+psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess,
+ PVOID *retBuffer) {
+ static ULONG initialBufferSize = 0x4000;
+ NTSTATUS status;
+ PVOID buffer;
+ ULONG bufferSize;
+ PSYSTEM_PROCESS_INFORMATION process;
+
+ bufferSize = initialBufferSize;
+ buffer = malloc(bufferSize);
+ if (buffer == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ while (TRUE) {
+ status = NtQuerySystemInformation(
+ SystemProcessInformation,
+ buffer,
+ bufferSize,
+ &bufferSize);
+ if (status == STATUS_BUFFER_TOO_SMALL ||
+ status == STATUS_INFO_LENGTH_MISMATCH)
+ {
+ free(buffer);
+ buffer = malloc(bufferSize);
+ if (buffer == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ }
+ else {
+ break;
+ }
+ }
+
+ if (! NT_SUCCESS(status)) {
+ psutil_SetFromNTStatusErr(
+ status, "NtQuerySystemInformation(SystemProcessInformation)");
+ goto error;
+ }
+
+ if (bufferSize <= 0x20000)
+ initialBufferSize = bufferSize;
+
+ process = PSUTIL_FIRST_PROCESS(buffer);
+ do {
+ if ((ULONG_PTR)process->UniqueProcessId == pid) {
+ *retProcess = process;
+ *retBuffer = buffer;
+ return 1;
+ }
+ } while ((process = PSUTIL_NEXT_PROCESS(process)));
+
+ NoSuchProcess("NtQuerySystemInformation (no PID found)");
+ goto error;
+
+error:
+ if (buffer != NULL)
+ free(buffer);
+ return 0;
+}
+
+
+/*
+ * Get various process information by using NtQuerySystemInformation.
+ * We use this as a fallback when faster functions fail with access
+ * denied. This is slower because it iterates over all processes.
+ * Returned tuple includes the following process info:
+ *
+ * - num_threads()
+ * - ctx_switches()
+ * - num_handles() (fallback)
+ * - cpu_times() (fallback)
+ * - create_time() (fallback)
+ * - io_counters() (fallback)
+ * - memory_info() (fallback)
+ */
+PyObject *
+psutil_proc_info(PyObject *self, PyObject *args) {
+ DWORD pid;
+ PSYSTEM_PROCESS_INFORMATION process;
+ PVOID buffer;
+ ULONG i;
+ ULONG ctx_switches = 0;
+ double user_time;
+ double kernel_time;
+ double create_time;
+ PyObject *py_retlist;
+
+ 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++)
+ ctx_switches += process->Threads[i].ContextSwitches;
+ user_time = (double)process->UserTime.HighPart * HI_T + \
+ (double)process->UserTime.LowPart * LO_T;
+ kernel_time = (double)process->KernelTime.HighPart * HI_T + \
+ (double)process->KernelTime.LowPart * LO_T;
+
+ // Convert the LARGE_INTEGER union to a Unix time.
+ // It's the best I could find by googling and borrowing code here
+ // and there. The time returned has a precision of 1 second.
+ if (0 == pid || 4 == pid) {
+ // the python module will translate this into BOOT_TIME later
+ create_time = 0;
+ }
+ else {
+ create_time = psutil_LargeIntegerToUnixTime(process->CreateTime);
+ }
+
+ py_retlist = Py_BuildValue(
+#if defined(_WIN64)
+ "kkdddkKKKKKK" "kKKKKKKKKK",
+#else
+ "kkdddkKKKKKK" "kIIIIIIIII",
+#endif
+ process->HandleCount, // num handles
+ ctx_switches, // num ctx switches
+ user_time, // cpu user time
+ kernel_time, // cpu kernel time
+ create_time, // create time
+ process->NumberOfThreads, // num threads
+ // IO counters
+ process->ReadOperationCount.QuadPart, // io rcount
+ process->WriteOperationCount.QuadPart, // io wcount
+ process->ReadTransferCount.QuadPart, // io rbytes
+ process->WriteTransferCount.QuadPart, // io wbytes
+ process->OtherOperationCount.QuadPart, // io others count
+ process->OtherTransferCount.QuadPart, // io others bytes
+ // memory
+ process->PageFaultCount, // num page faults
+ process->PeakWorkingSetSize, // peak wset
+ process->WorkingSetSize, // wset
+ process->QuotaPeakPagedPoolUsage, // peak paged pool
+ process->QuotaPagedPoolUsage, // paged pool
+ process->QuotaPeakNonPagedPoolUsage, // peak non paged pool
+ process->QuotaNonPagedPoolUsage, // non paged pool
+ process->PagefileUsage, // pagefile
+ process->PeakPagefileUsage, // peak pagefile
+ process->PrivatePageCount // private
+ );
+
+ free(buffer);
+ return py_retlist;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_info.h b/contrib/python/psutil/py3/psutil/arch/windows/process_info.h
new file mode 100644
index 0000000000..5e89ddebdf
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/process_info.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>
+
+#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)
+
+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_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/process_utils.c
new file mode 100644
index 0000000000..acbda301a4
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/process_utils.c
@@ -0,0 +1,189 @@
+/*
+ * 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 process functions.
+ */
+
+#include <Python.h>
+#include <windows.h>
+#include <Psapi.h> // EnumProcesses
+
+#include "../../_psutil_common.h"
+#include "process_utils.h"
+
+
+DWORD *
+psutil_get_pids(DWORD *numberOfReturnedPIDs) {
+ // Win32 SDK says the only way to know if our process array
+ // wasn't large enough is to check the returned size and make
+ // sure that it doesn't match the size of the array.
+ // If it does we allocate a larger array and try again
+
+ // Stores the actual array
+ DWORD *procArray = NULL;
+ DWORD procArrayByteSz;
+ int procArraySz = 0;
+
+ // Stores the byte size of the returned array from enumprocesses
+ DWORD enumReturnSz = 0;
+
+ do {
+ procArraySz += 1024;
+ if (procArray != NULL)
+ free(procArray);
+ procArrayByteSz = procArraySz * sizeof(DWORD);
+ procArray = malloc(procArrayByteSz);
+ if (procArray == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ if (! EnumProcesses(procArray, procArrayByteSz, &enumReturnSz)) {
+ free(procArray);
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ }
+ } while (enumReturnSz == procArraySz * sizeof(DWORD));
+
+ // The number of elements is the returned size / size of each element
+ *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD);
+
+ return procArray;
+}
+
+
+// Return 1 if PID exists, 0 if not, -1 on error.
+int
+psutil_pid_in_pids(DWORD pid) {
+ DWORD *proclist = NULL;
+ DWORD numberOfReturnedPIDs;
+ DWORD i;
+
+ proclist = psutil_get_pids(&numberOfReturnedPIDs);
+ if (proclist == NULL) {
+ psutil_debug("psutil_get_pids() failed");
+ return -1;
+ }
+ for (i = 0; i < numberOfReturnedPIDs; i++) {
+ if (proclist[i] == pid) {
+ free(proclist);
+ return 1;
+ }
+ }
+ free(proclist);
+ return 0;
+}
+
+
+// Given a process handle checks whether it's actually running. If it
+// does return the handle, else return NULL with Python exception set.
+// This is needed because OpenProcess API sucks.
+HANDLE
+psutil_check_phandle(HANDLE hProcess, DWORD pid, int check_exit_code) {
+ DWORD exitCode;
+
+ if (hProcess == NULL) {
+ if (GetLastError() == ERROR_INVALID_PARAMETER) {
+ // Yeah, this is the actual error code in case of
+ // "no such process".
+ NoSuchProcess("OpenProcess -> ERROR_INVALID_PARAMETER");
+ return NULL;
+ }
+ if (GetLastError() == ERROR_SUCCESS) {
+ // Yeah, it's this bad.
+ // https://github.com/giampaolo/psutil/issues/1877
+ if (psutil_pid_in_pids(pid) == 1) {
+ psutil_debug("OpenProcess -> ERROR_SUCCESS turned into AD");
+ AccessDenied("OpenProcess -> ERROR_SUCCESS");
+ }
+ else {
+ psutil_debug("OpenProcess -> ERROR_SUCCESS turned into NSP");
+ NoSuchProcess("OpenProcess -> ERROR_SUCCESS");
+ }
+ return NULL;
+ }
+ PyErr_SetFromOSErrnoWithSyscall("OpenProcess");
+ return NULL;
+ }
+
+ if (check_exit_code == 0)
+ return hProcess;
+
+ if (GetExitCodeProcess(hProcess, &exitCode)) {
+ // XXX - maybe STILL_ACTIVE is not fully reliable as per:
+ // http://stackoverflow.com/questions/1591342/#comment47830782_1591379
+ if (exitCode == STILL_ACTIVE) {
+ return hProcess;
+ }
+ if (psutil_pid_in_pids(pid) == 1) {
+ return hProcess;
+ }
+ CloseHandle(hProcess);
+ NoSuchProcess("GetExitCodeProcess != STILL_ACTIVE");
+ return NULL;
+ }
+
+ if (GetLastError() == ERROR_ACCESS_DENIED) {
+ psutil_debug("GetExitCodeProcess -> ERROR_ACCESS_DENIED (ignored)");
+ SetLastError(0);
+ return hProcess;
+ }
+ PyErr_SetFromOSErrnoWithSyscall("GetExitCodeProcess");
+ CloseHandle(hProcess);
+ return NULL;
+}
+
+
+// A wrapper around OpenProcess setting NSP exception if process no
+// longer exists. *pid* is the process PID, *access* is the first
+// argument to OpenProcess.
+// Return a process handle or NULL with exception set.
+HANDLE
+psutil_handle_from_pid(DWORD pid, DWORD access) {
+ HANDLE hProcess;
+
+ if (pid == 0) {
+ // otherwise we'd get NoSuchProcess
+ return AccessDenied("automatically set for PID 0");
+ }
+
+ hProcess = OpenProcess(access, FALSE, pid);
+
+ if ((hProcess == NULL) && (GetLastError() == ERROR_ACCESS_DENIED)) {
+ PyErr_SetFromOSErrnoWithSyscall("OpenProcess");
+ return NULL;
+ }
+
+ hProcess = psutil_check_phandle(hProcess, pid, 1);
+ return hProcess;
+}
+
+
+// Check for PID existance. Return 1 if pid exists, 0 if not, -1 on error.
+int
+psutil_pid_is_running(DWORD pid) {
+ HANDLE hProcess;
+
+ // Special case for PID 0 System Idle Process
+ if (pid == 0)
+ return 1;
+ if (pid < 0)
+ return 0;
+
+ hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
+
+ // Access denied means there's a process to deny access to.
+ if ((hProcess == NULL) && (GetLastError() == ERROR_ACCESS_DENIED))
+ return 1;
+
+ hProcess = psutil_check_phandle(hProcess, pid, 1);
+ if (hProcess != NULL) {
+ CloseHandle(hProcess);
+ return 1;
+ }
+
+ CloseHandle(hProcess);
+ PyErr_Clear();
+ return psutil_pid_in_pids(pid);
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/process_utils.h b/contrib/python/psutil/py3/psutil/arch/windows/process_utils.h
new file mode 100644
index 0000000000..dca7c991a5
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/process_utils.h
@@ -0,0 +1,12 @@
+/*
+ * 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.
+ */
+
+DWORD* psutil_get_pids(DWORD *numberOfReturnedPIDs);
+HANDLE psutil_handle_from_pid(DWORD pid, DWORD dwDesiredAccess);
+HANDLE psutil_check_phandle(HANDLE hProcess, DWORD pid, int check_exit_code);
+int psutil_pid_is_running(DWORD pid);
+int psutil_assert_pid_exists(DWORD pid, char *err);
+int psutil_assert_pid_not_exists(DWORD pid, char *err);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/security.c b/contrib/python/psutil/py3/psutil/arch/windows/security.c
new file mode 100644
index 0000000000..7e400a2541
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/security.c
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ *
+ * Security related functions for Windows platform (Set privileges such as
+ * SE DEBUG).
+ */
+
+#include <windows.h>
+#include <Python.h>
+
+#include "../../_psutil_common.h"
+
+
+static BOOL
+psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) {
+ TOKEN_PRIVILEGES tp;
+ LUID luid;
+ TOKEN_PRIVILEGES tpPrevious;
+ DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES);
+
+ if (! LookupPrivilegeValue(NULL, Privilege, &luid)) {
+ PyErr_SetFromOSErrnoWithSyscall("LookupPrivilegeValue");
+ return 1;
+ }
+
+ // first pass. get current privilege setting
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Luid = luid;
+ tp.Privileges[0].Attributes = 0;
+
+ if (! AdjustTokenPrivileges(
+ hToken,
+ FALSE,
+ &tp,
+ sizeof(TOKEN_PRIVILEGES),
+ &tpPrevious,
+ &cbPrevious))
+ {
+ PyErr_SetFromOSErrnoWithSyscall("AdjustTokenPrivileges");
+ return 1;
+ }
+
+ // Second pass. Set privilege based on previous setting.
+ tpPrevious.PrivilegeCount = 1;
+ tpPrevious.Privileges[0].Luid = luid;
+
+ if (bEnablePrivilege)
+ tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
+ else
+ tpPrevious.Privileges[0].Attributes ^=
+ (SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes);
+
+ if (! AdjustTokenPrivileges(
+ hToken,
+ FALSE,
+ &tpPrevious,
+ cbPrevious,
+ NULL,
+ NULL))
+ {
+ PyErr_SetFromOSErrnoWithSyscall("AdjustTokenPrivileges");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static HANDLE
+psutil_get_thisproc_token() {
+ HANDLE hToken = NULL;
+ HANDLE me = GetCurrentProcess();
+
+ if (! OpenProcessToken(
+ me, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
+ {
+ if (GetLastError() == ERROR_NO_TOKEN)
+ {
+ if (! ImpersonateSelf(SecurityImpersonation)) {
+ PyErr_SetFromOSErrnoWithSyscall("ImpersonateSelf");
+ return NULL;
+ }
+ if (! OpenProcessToken(
+ me, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
+ {
+ PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken");
+ return NULL;
+ }
+ }
+ else {
+ PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken");
+ return NULL;
+ }
+ }
+
+ return hToken;
+}
+
+
+static void
+psutil_print_err() {
+ char *msg = "psutil module couldn't set SE DEBUG mode for this process; " \
+ "please file an issue against psutil bug tracker";
+ psutil_debug(msg);
+ if (GetLastError() != ERROR_ACCESS_DENIED)
+ PyErr_WarnEx(PyExc_RuntimeWarning, msg, 1);
+ PyErr_Clear();
+}
+
+
+/*
+ * Set this process in SE DEBUG mode so that we have more chances of
+ * querying processes owned by other users, including many owned by
+ * Administrator and Local System.
+ * https://docs.microsoft.com/windows-hardware/drivers/debugger/debug-privilege
+ * This is executed on module import and we don't crash on error.
+ */
+int
+psutil_set_se_debug() {
+ HANDLE hToken;
+
+ if ((hToken = psutil_get_thisproc_token()) == NULL) {
+ // "return 1;" to get an exception
+ psutil_print_err();
+ return 0;
+ }
+
+ if (psutil_set_privilege(hToken, SE_DEBUG_NAME, TRUE) != 0) {
+ // "return 1;" to get an exception
+ psutil_print_err();
+ }
+
+ RevertToSelf();
+ CloseHandle(hToken);
+ return 0;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/security.h b/contrib/python/psutil/py3/psutil/arch/windows/security.h
new file mode 100644
index 0000000000..8d4ddb00d4
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/security.h
@@ -0,0 +1,13 @@
+/*
+ * 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.
+ *
+ * Security related functions for Windows platform (Set privileges such as
+ * SeDebug), as well as security helper functions.
+ */
+
+#include <windows.h>
+
+int psutil_set_se_debug();
+
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/services.c b/contrib/python/psutil/py3/psutil/arch/windows/services.c
new file mode 100644
index 0000000000..a91cb8f797
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/services.c
@@ -0,0 +1,479 @@
+/*
+ * 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 <windows.h>
+#include <Winsvc.h>
+
+#include "../../_psutil_common.h"
+#include "services.h"
+
+
+// ==================================================================
+// utils
+// ==================================================================
+
+SC_HANDLE
+psutil_get_service_handler(char *service_name, DWORD scm_access, DWORD access)
+{
+ SC_HANDLE sc = NULL;
+ SC_HANDLE hService = NULL;
+
+ sc = OpenSCManager(NULL, NULL, scm_access);
+ if (sc == NULL) {
+ PyErr_SetFromOSErrnoWithSyscall("OpenSCManager");
+ return NULL;
+ }
+ hService = OpenService(sc, service_name, access);
+ if (hService == NULL) {
+ PyErr_SetFromOSErrnoWithSyscall("OpenService");
+ CloseServiceHandle(sc);
+ return NULL;
+ }
+ CloseServiceHandle(sc);
+ return hService;
+}
+
+
+// XXX - expose these as constants?
+static const char *
+get_startup_string(DWORD startup) {
+ switch (startup) {
+ case SERVICE_AUTO_START:
+ return "automatic";
+ case SERVICE_DEMAND_START:
+ return "manual";
+ case SERVICE_DISABLED:
+ return "disabled";
+/*
+ // drivers only (since we use EnumServicesStatusEx() with
+ // SERVICE_WIN32)
+ case SERVICE_BOOT_START:
+ return "boot-start";
+ case SERVICE_SYSTEM_START:
+ return "system-start";
+*/
+ default:
+ return "unknown";
+ }
+}
+
+
+// XXX - expose these as constants?
+static const char *
+get_state_string(DWORD state) {
+ switch (state) {
+ case SERVICE_RUNNING:
+ return "running";
+ case SERVICE_PAUSED:
+ return "paused";
+ case SERVICE_START_PENDING:
+ return "start_pending";
+ case SERVICE_PAUSE_PENDING:
+ return "pause_pending";
+ case SERVICE_CONTINUE_PENDING:
+ return "continue_pending";
+ case SERVICE_STOP_PENDING:
+ return "stop_pending";
+ case SERVICE_STOPPED:
+ return "stopped";
+ default:
+ return "unknown";
+ }
+}
+
+
+// ==================================================================
+// APIs
+// ==================================================================
+
+/*
+ * Enumerate all services.
+ */
+PyObject *
+psutil_winservice_enumerate(PyObject *self, PyObject *args) {
+ ENUM_SERVICE_STATUS_PROCESSW *lpService = NULL;
+ BOOL ok;
+ SC_HANDLE sc = NULL;
+ DWORD bytesNeeded = 0;
+ DWORD srvCount;
+ DWORD resumeHandle = 0;
+ DWORD dwBytes = 0;
+ DWORD i;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_name = NULL;
+ PyObject *py_display_name = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ sc = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
+ if (sc == NULL) {
+ PyErr_SetFromOSErrnoWithSyscall("OpenSCManager");
+ return NULL;
+ }
+
+ for (;;) {
+ ok = EnumServicesStatusExW(
+ sc,
+ SC_ENUM_PROCESS_INFO,
+ SERVICE_WIN32, // XXX - extend this to include drivers etc.?
+ SERVICE_STATE_ALL,
+ (LPBYTE)lpService,
+ dwBytes,
+ &bytesNeeded,
+ &srvCount,
+ &resumeHandle,
+ NULL);
+ if (ok || (GetLastError() != ERROR_MORE_DATA))
+ break;
+ if (lpService)
+ free(lpService);
+ dwBytes = bytesNeeded;
+ lpService = (ENUM_SERVICE_STATUS_PROCESSW*)malloc(dwBytes);
+ }
+
+ for (i = 0; i < srvCount; i++) {
+ // Get unicode name / display name.
+ py_name = NULL;
+ py_name = PyUnicode_FromWideChar(
+ lpService[i].lpServiceName, wcslen(lpService[i].lpServiceName));
+ if (py_name == NULL)
+ goto error;
+
+ py_display_name = NULL;
+ py_display_name = PyUnicode_FromWideChar(
+ lpService[i].lpDisplayName, wcslen(lpService[i].lpDisplayName));
+ if (py_display_name == NULL)
+ goto error;
+
+ // Construct the result.
+ py_tuple = Py_BuildValue("(OO)", py_name, py_display_name);
+ if (py_tuple == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_display_name);
+ Py_DECREF(py_name);
+ Py_DECREF(py_tuple);
+ }
+
+ // Free resources.
+ CloseServiceHandle(sc);
+ free(lpService);
+ return py_retlist;
+
+error:
+ Py_DECREF(py_name);
+ Py_XDECREF(py_display_name);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (sc != NULL)
+ CloseServiceHandle(sc);
+ if (lpService != NULL)
+ free(lpService);
+ return NULL;
+}
+
+
+/*
+ * Get service config information. Returns:
+ * - display_name
+ * - binpath
+ * - username
+ * - startup_type
+ */
+PyObject *
+psutil_winservice_query_config(PyObject *self, PyObject *args) {
+ char *service_name;
+ SC_HANDLE hService = NULL;
+ BOOL ok;
+ DWORD bytesNeeded = 0;
+ QUERY_SERVICE_CONFIGW *qsc = NULL;
+ PyObject *py_tuple = NULL;
+ PyObject *py_unicode_display_name = NULL;
+ PyObject *py_unicode_binpath = NULL;
+ PyObject *py_unicode_username = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &service_name))
+ return NULL;
+ hService = psutil_get_service_handler(
+ service_name, SC_MANAGER_ENUMERATE_SERVICE, SERVICE_QUERY_CONFIG);
+ if (hService == NULL)
+ goto error;
+
+ // First call to QueryServiceConfigW() is necessary to get the
+ // right size.
+ bytesNeeded = 0;
+ QueryServiceConfigW(hService, NULL, 0, &bytesNeeded);
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfigW");
+ goto error;
+ }
+ qsc = (QUERY_SERVICE_CONFIGW *)malloc(bytesNeeded);
+ ok = QueryServiceConfigW(hService, qsc, bytesNeeded, &bytesNeeded);
+ if (ok == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfigW");
+ goto error;
+ }
+
+ // Get unicode display name.
+ py_unicode_display_name = PyUnicode_FromWideChar(
+ qsc->lpDisplayName, wcslen(qsc->lpDisplayName));
+ if (py_unicode_display_name == NULL)
+ goto error;
+
+ // Get unicode bin path.
+ py_unicode_binpath = PyUnicode_FromWideChar(
+ qsc->lpBinaryPathName, wcslen(qsc->lpBinaryPathName));
+ if (py_unicode_binpath == NULL)
+ goto error;
+
+ // Get unicode username.
+ py_unicode_username = PyUnicode_FromWideChar(
+ qsc->lpServiceStartName, wcslen(qsc->lpServiceStartName));
+ if (py_unicode_username == NULL)
+ goto error;
+
+ // Construct result tuple.
+ py_tuple = Py_BuildValue(
+ "(OOOs)",
+ py_unicode_display_name,
+ py_unicode_binpath,
+ py_unicode_username,
+ get_startup_string(qsc->dwStartType) // startup
+ );
+ if (py_tuple == NULL)
+ goto error;
+
+ // Free resources.
+ Py_DECREF(py_unicode_display_name);
+ Py_DECREF(py_unicode_binpath);
+ Py_DECREF(py_unicode_username);
+ free(qsc);
+ CloseServiceHandle(hService);
+ return py_tuple;
+
+error:
+ Py_XDECREF(py_unicode_display_name);
+ Py_XDECREF(py_unicode_binpath);
+ Py_XDECREF(py_unicode_username);
+ Py_XDECREF(py_tuple);
+ if (hService != NULL)
+ CloseServiceHandle(hService);
+ if (qsc != NULL)
+ free(qsc);
+ return NULL;
+}
+
+
+/*
+ * Get service status information. Returns:
+ * - status
+ * - pid
+ */
+PyObject *
+psutil_winservice_query_status(PyObject *self, PyObject *args) {
+ char *service_name;
+ SC_HANDLE hService = NULL;
+ BOOL ok;
+ DWORD bytesNeeded = 0;
+ SERVICE_STATUS_PROCESS *ssp = NULL;
+ PyObject *py_tuple = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &service_name))
+ return NULL;
+ hService = psutil_get_service_handler(
+ service_name, SC_MANAGER_ENUMERATE_SERVICE, SERVICE_QUERY_STATUS);
+ if (hService == NULL)
+ goto error;
+
+ // First call to QueryServiceStatusEx() is necessary to get the
+ // right size.
+ QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, NULL, 0,
+ &bytesNeeded);
+ if (GetLastError() == ERROR_MUI_FILE_NOT_FOUND) {
+ // Also services.msc fails in the same manner, so we return an
+ // empty string.
+ CloseServiceHandle(hService);
+ return Py_BuildValue("s", "");
+ }
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ PyErr_SetFromOSErrnoWithSyscall("QueryServiceStatusEx");
+ goto error;
+ }
+ ssp = (SERVICE_STATUS_PROCESS *)HeapAlloc(
+ GetProcessHeap(), 0, bytesNeeded);
+ if (ssp == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ // Actual call.
+ ok = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)ssp,
+ bytesNeeded, &bytesNeeded);
+ if (ok == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("QueryServiceStatusEx");
+ goto error;
+ }
+
+ py_tuple = Py_BuildValue(
+ "(sk)",
+ get_state_string(ssp->dwCurrentState),
+ ssp->dwProcessId
+ );
+ if (py_tuple == NULL)
+ goto error;
+
+ CloseServiceHandle(hService);
+ HeapFree(GetProcessHeap(), 0, ssp);
+ return py_tuple;
+
+error:
+ Py_XDECREF(py_tuple);
+ if (hService != NULL)
+ CloseServiceHandle(hService);
+ if (ssp != NULL)
+ HeapFree(GetProcessHeap(), 0, ssp);
+ return NULL;
+}
+
+
+/*
+ * Get service description.
+ */
+PyObject *
+psutil_winservice_query_descr(PyObject *self, PyObject *args) {
+ ENUM_SERVICE_STATUS_PROCESSW *lpService = NULL;
+ BOOL ok;
+ DWORD bytesNeeded = 0;
+ SC_HANDLE hService = NULL;
+ SERVICE_DESCRIPTIONW *scd = NULL;
+ char *service_name;
+ PyObject *py_retstr = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &service_name))
+ return NULL;
+ hService = psutil_get_service_handler(
+ service_name, SC_MANAGER_ENUMERATE_SERVICE, SERVICE_QUERY_CONFIG);
+ if (hService == NULL)
+ goto error;
+
+ // This first call to QueryServiceConfig2W() is necessary in order
+ // to get the right size.
+ bytesNeeded = 0;
+ QueryServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION, NULL, 0,
+ &bytesNeeded);
+ if (GetLastError() == ERROR_MUI_FILE_NOT_FOUND) {
+ // Also services.msc fails in the same manner, so we return an
+ // empty string.
+ CloseServiceHandle(hService);
+ return Py_BuildValue("s", "");
+ }
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfig2W");
+ goto error;
+ }
+
+ scd = (SERVICE_DESCRIPTIONW *)malloc(bytesNeeded);
+ ok = QueryServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION,
+ (LPBYTE)scd, bytesNeeded, &bytesNeeded);
+ if (ok == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("QueryServiceConfig2W");
+ goto error;
+ }
+
+ if (scd->lpDescription == NULL) {
+ py_retstr = Py_BuildValue("s", "");
+ }
+ else {
+ py_retstr = PyUnicode_FromWideChar(
+ scd->lpDescription, wcslen(scd->lpDescription));
+ }
+ if (!py_retstr)
+ goto error;
+
+ free(scd);
+ CloseServiceHandle(hService);
+ return py_retstr;
+
+error:
+ if (hService != NULL)
+ CloseServiceHandle(hService);
+ if (lpService != NULL)
+ free(lpService);
+ return NULL;
+}
+
+
+/*
+ * Start service.
+ * XXX - note: this is exposed but not used.
+ */
+PyObject *
+psutil_winservice_start(PyObject *self, PyObject *args) {
+ char *service_name;
+ BOOL ok;
+ SC_HANDLE hService = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &service_name))
+ return NULL;
+ hService = psutil_get_service_handler(
+ service_name, SC_MANAGER_ALL_ACCESS, SERVICE_START);
+ if (hService == NULL) {
+ goto error;
+ }
+ ok = StartService(hService, 0, NULL);
+ if (ok == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("StartService");
+ goto error;
+ }
+
+ Py_RETURN_NONE;
+
+error:
+ if (hService != NULL)
+ CloseServiceHandle(hService);
+ return NULL;
+}
+
+
+/*
+ * Stop service.
+ * XXX - note: this is exposed but not used.
+ */
+PyObject *
+psutil_winservice_stop(PyObject *self, PyObject *args) {
+ char *service_name;
+ BOOL ok;
+ SC_HANDLE hService = NULL;
+ SERVICE_STATUS ssp;
+
+ if (!PyArg_ParseTuple(args, "s", &service_name))
+ return NULL;
+ hService = psutil_get_service_handler(
+ service_name, SC_MANAGER_ALL_ACCESS, SERVICE_STOP);
+ if (hService == NULL)
+ goto error;
+
+ // Note: this can hang for 30 secs.
+ Py_BEGIN_ALLOW_THREADS
+ ok = ControlService(hService, SERVICE_CONTROL_STOP, &ssp);
+ Py_END_ALLOW_THREADS
+ if (ok == 0) {
+ PyErr_SetFromOSErrnoWithSyscall("ControlService");
+ goto error;
+ }
+
+ CloseServiceHandle(hService);
+ Py_RETURN_NONE;
+
+error:
+ if (hService != NULL)
+ CloseServiceHandle(hService);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/services.h b/contrib/python/psutil/py3/psutil/arch/windows/services.h
new file mode 100644
index 0000000000..ebcfa5ef59
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/services.h
@@ -0,0 +1,17 @@
+/*
+ * 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 <Winsvc.h>
+
+SC_HANDLE psutil_get_service_handle(
+ char service_name, DWORD scm_access, DWORD access);
+PyObject *psutil_winservice_enumerate(PyObject *self, PyObject *args);
+PyObject *psutil_winservice_query_config(PyObject *self, PyObject *args);
+PyObject *psutil_winservice_query_status(PyObject *self, PyObject *args);
+PyObject *psutil_winservice_query_descr(PyObject *self, PyObject *args);
+PyObject *psutil_winservice_start(PyObject *self, PyObject *args);
+PyObject *psutil_winservice_stop(PyObject *self, PyObject *args);
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/socks.c b/contrib/python/psutil/py3/psutil/arch/windows/socks.c
new file mode 100644
index 0000000000..5e4c2df802
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/socks.c
@@ -0,0 +1,471 @@
+/*
+ * 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.
+ */
+
+// Fixes clash between winsock2.h and windows.h
+#define WIN32_LEAN_AND_MEAN
+
+#include <Python.h>
+#include <windows.h>
+#include <ws2tcpip.h>
+
+#include "../../_psutil_common.h"
+#include "process_utils.h"
+
+
+#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff)
+#define STATUS_UNSUCCESSFUL 0xC0000001
+
+ULONG g_TcpTableSize = 0;
+ULONG g_UdpTableSize = 0;
+
+
+// Note about GetExtended[Tcp|Udp]Table syscalls: due to other processes
+// being active on the machine, it's possible that the size of the table
+// increases between the moment we query the size and the moment we query
+// the data. Therefore we retry if that happens. See:
+// https://github.com/giampaolo/psutil/pull/1335
+// https://github.com/giampaolo/psutil/issues/1294
+// A global and ever increasing size is used in order to avoid calling
+// GetExtended[Tcp|Udp]Table twice per call (faster).
+
+
+static PVOID __GetExtendedTcpTable(ULONG family) {
+ DWORD err;
+ PVOID table;
+ ULONG size;
+ TCP_TABLE_CLASS class = TCP_TABLE_OWNER_PID_ALL;
+
+ size = g_TcpTableSize;
+ if (size == 0) {
+ GetExtendedTcpTable(NULL, &size, FALSE, family, class, 0);
+ // reserve 25% more space
+ size = size + (size / 2 / 2);
+ g_TcpTableSize = size;
+ }
+
+ table = malloc(size);
+ if (table == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ err = GetExtendedTcpTable(table, &size, FALSE, family, class, 0);
+ if (err == NO_ERROR)
+ return table;
+
+ free(table);
+ if (err == ERROR_INSUFFICIENT_BUFFER || err == STATUS_UNSUCCESSFUL) {
+ psutil_debug("GetExtendedTcpTable: retry with different bufsize");
+ g_TcpTableSize = 0;
+ return __GetExtendedTcpTable(family);
+ }
+
+ PyErr_SetString(PyExc_RuntimeError, "GetExtendedTcpTable failed");
+ return NULL;
+}
+
+
+static PVOID __GetExtendedUdpTable(ULONG family) {
+ DWORD err;
+ PVOID table;
+ ULONG size;
+ UDP_TABLE_CLASS class = UDP_TABLE_OWNER_PID;
+
+ size = g_UdpTableSize;
+ if (size == 0) {
+ GetExtendedUdpTable(NULL, &size, FALSE, family, class, 0);
+ // reserve 25% more space
+ size = size + (size / 2 / 2);
+ g_UdpTableSize = size;
+ }
+
+ table = malloc(size);
+ if (table == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ err = GetExtendedUdpTable(table, &size, FALSE, family, class, 0);
+ if (err == NO_ERROR)
+ return table;
+
+ free(table);
+ if (err == ERROR_INSUFFICIENT_BUFFER || err == STATUS_UNSUCCESSFUL) {
+ psutil_debug("GetExtendedUdpTable: retry with different bufsize");
+ g_UdpTableSize = 0;
+ return __GetExtendedUdpTable(family);
+ }
+
+ PyErr_SetString(PyExc_RuntimeError, "GetExtendedUdpTable failed");
+ return NULL;
+}
+
+
+#define psutil_conn_decref_objs() \
+ Py_DECREF(_AF_INET); \
+ Py_DECREF(_AF_INET6);\
+ Py_DECREF(_SOCK_STREAM);\
+ Py_DECREF(_SOCK_DGRAM);
+
+
+/*
+ * Return a list of network connections opened by a process
+ */
+PyObject *
+psutil_net_connections(PyObject *self, PyObject *args) {
+ static long null_address[4] = { 0, 0, 0, 0 };
+ DWORD pid;
+ int pid_return;
+ PVOID table = NULL;
+ PMIB_TCPTABLE_OWNER_PID tcp4Table;
+ PMIB_UDPTABLE_OWNER_PID udp4Table;
+ PMIB_TCP6TABLE_OWNER_PID tcp6Table;
+ PMIB_UDP6TABLE_OWNER_PID udp6Table;
+ ULONG i;
+ CHAR addressBufferLocal[65];
+ CHAR addressBufferRemote[65];
+
+ PyObject *py_retlist = NULL;
+ PyObject *py_conn_tuple = NULL;
+ PyObject *py_af_filter = NULL;
+ PyObject *py_type_filter = NULL;
+ PyObject *py_addr_tuple_local = NULL;
+ PyObject *py_addr_tuple_remote = NULL;
+ PyObject *_AF_INET = PyLong_FromLong((long)AF_INET);
+ PyObject *_AF_INET6 = PyLong_FromLong((long)AF_INET6);
+ PyObject *_SOCK_STREAM = PyLong_FromLong((long)SOCK_STREAM);
+ PyObject *_SOCK_DGRAM = PyLong_FromLong((long)SOCK_DGRAM);
+
+ 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)) {
+ psutil_conn_decref_objs();
+ PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
+ return NULL;
+ }
+
+ if (pid != -1) {
+ pid_return = psutil_pid_is_running(pid);
+ if (pid_return == 0) {
+ psutil_conn_decref_objs();
+ return NoSuchProcess("psutil_pid_is_running");
+ }
+ else if (pid_return == -1) {
+ psutil_conn_decref_objs();
+ return NULL;
+ }
+ }
+
+ py_retlist = PyList_New(0);
+ if (py_retlist == NULL) {
+ psutil_conn_decref_objs();
+ return NULL;
+ }
+
+ // TCP IPv4
+
+ if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) &&
+ (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1))
+ {
+ table = NULL;
+ py_conn_tuple = NULL;
+ py_addr_tuple_local = NULL;
+ py_addr_tuple_remote = NULL;
+
+ table = __GetExtendedTcpTable(AF_INET);
+ if (table == NULL)
+ goto error;
+ tcp4Table = table;
+ for (i = 0; i < tcp4Table->dwNumEntries; i++) {
+ if (pid != -1) {
+ if (tcp4Table->table[i].dwOwningPid != pid) {
+ continue;
+ }
+ }
+
+ if (tcp4Table->table[i].dwLocalAddr != 0 ||
+ tcp4Table->table[i].dwLocalPort != 0)
+ {
+ struct in_addr addr;
+
+ addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr;
+ RtlIpv4AddressToStringA(&addr, addressBufferLocal);
+ py_addr_tuple_local = Py_BuildValue(
+ "(si)",
+ addressBufferLocal,
+ BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort));
+ }
+ else {
+ py_addr_tuple_local = PyTuple_New(0);
+ }
+
+ if (py_addr_tuple_local == NULL)
+ goto error;
+
+ // On Windows <= XP, remote addr is filled even if socket
+ // is in LISTEN mode in which case we just ignore it.
+ if ((tcp4Table->table[i].dwRemoteAddr != 0 ||
+ tcp4Table->table[i].dwRemotePort != 0) &&
+ (tcp4Table->table[i].dwState != MIB_TCP_STATE_LISTEN))
+ {
+ struct in_addr addr;
+
+ addr.S_un.S_addr = tcp4Table->table[i].dwRemoteAddr;
+ RtlIpv4AddressToStringA(&addr, addressBufferRemote);
+ py_addr_tuple_remote = Py_BuildValue(
+ "(si)",
+ addressBufferRemote,
+ BYTESWAP_USHORT(tcp4Table->table[i].dwRemotePort));
+ }
+ else
+ {
+ py_addr_tuple_remote = PyTuple_New(0);
+ }
+
+ if (py_addr_tuple_remote == NULL)
+ goto error;
+
+ py_conn_tuple = Py_BuildValue(
+ "(iiiNNiI)",
+ -1,
+ AF_INET,
+ SOCK_STREAM,
+ py_addr_tuple_local,
+ py_addr_tuple_remote,
+ tcp4Table->table[i].dwState,
+ tcp4Table->table[i].dwOwningPid);
+ if (!py_conn_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_conn_tuple))
+ goto error;
+ Py_CLEAR(py_conn_tuple);
+ }
+
+ free(table);
+ table = NULL;
+ }
+
+ // TCP IPv6
+ if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) &&
+ (PySequence_Contains(py_type_filter, _SOCK_STREAM) == 1) &&
+ (RtlIpv6AddressToStringA != NULL))
+ {
+ table = NULL;
+ py_conn_tuple = NULL;
+ py_addr_tuple_local = NULL;
+ py_addr_tuple_remote = NULL;
+
+ table = __GetExtendedTcpTable(AF_INET6);
+ if (table == NULL)
+ goto error;
+ tcp6Table = table;
+ for (i = 0; i < tcp6Table->dwNumEntries; i++)
+ {
+ if (pid != -1) {
+ if (tcp6Table->table[i].dwOwningPid != pid) {
+ continue;
+ }
+ }
+
+ if (memcmp(tcp6Table->table[i].ucLocalAddr, null_address, 16)
+ != 0 || tcp6Table->table[i].dwLocalPort != 0)
+ {
+ struct in6_addr addr;
+
+ memcpy(&addr, tcp6Table->table[i].ucLocalAddr, 16);
+ RtlIpv6AddressToStringA(&addr, addressBufferLocal);
+ py_addr_tuple_local = Py_BuildValue(
+ "(si)",
+ addressBufferLocal,
+ BYTESWAP_USHORT(tcp6Table->table[i].dwLocalPort));
+ }
+ else {
+ py_addr_tuple_local = PyTuple_New(0);
+ }
+
+ if (py_addr_tuple_local == NULL)
+ goto error;
+
+ // On Windows <= XP, remote addr is filled even if socket
+ // is in LISTEN mode in which case we just ignore it.
+ if ((memcmp(tcp6Table->table[i].ucRemoteAddr, null_address, 16)
+ != 0 ||
+ tcp6Table->table[i].dwRemotePort != 0) &&
+ (tcp6Table->table[i].dwState != MIB_TCP_STATE_LISTEN))
+ {
+ struct in6_addr addr;
+
+ memcpy(&addr, tcp6Table->table[i].ucRemoteAddr, 16);
+ RtlIpv6AddressToStringA(&addr, addressBufferRemote);
+ py_addr_tuple_remote = Py_BuildValue(
+ "(si)",
+ addressBufferRemote,
+ BYTESWAP_USHORT(tcp6Table->table[i].dwRemotePort));
+ }
+ else {
+ py_addr_tuple_remote = PyTuple_New(0);
+ }
+
+ if (py_addr_tuple_remote == NULL)
+ goto error;
+
+ py_conn_tuple = Py_BuildValue(
+ "(iiiNNiI)",
+ -1,
+ AF_INET6,
+ SOCK_STREAM,
+ py_addr_tuple_local,
+ py_addr_tuple_remote,
+ tcp6Table->table[i].dwState,
+ tcp6Table->table[i].dwOwningPid);
+ if (!py_conn_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_conn_tuple))
+ goto error;
+ Py_CLEAR(py_conn_tuple);
+ }
+
+ free(table);
+ table = NULL;
+ }
+
+ // UDP IPv4
+
+ if ((PySequence_Contains(py_af_filter, _AF_INET) == 1) &&
+ (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1))
+ {
+ table = NULL;
+ py_conn_tuple = NULL;
+ py_addr_tuple_local = NULL;
+ py_addr_tuple_remote = NULL;
+ table = __GetExtendedUdpTable(AF_INET);
+ if (table == NULL)
+ goto error;
+ udp4Table = table;
+ for (i = 0; i < udp4Table->dwNumEntries; i++)
+ {
+ if (pid != -1) {
+ if (udp4Table->table[i].dwOwningPid != pid) {
+ continue;
+ }
+ }
+
+ if (udp4Table->table[i].dwLocalAddr != 0 ||
+ udp4Table->table[i].dwLocalPort != 0)
+ {
+ struct in_addr addr;
+
+ addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr;
+ RtlIpv4AddressToStringA(&addr, addressBufferLocal);
+ py_addr_tuple_local = Py_BuildValue(
+ "(si)",
+ addressBufferLocal,
+ BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort));
+ }
+ else {
+ py_addr_tuple_local = PyTuple_New(0);
+ }
+
+ if (py_addr_tuple_local == NULL)
+ goto error;
+
+ py_conn_tuple = Py_BuildValue(
+ "(iiiNNiI)",
+ -1,
+ AF_INET,
+ SOCK_DGRAM,
+ py_addr_tuple_local,
+ PyTuple_New(0),
+ PSUTIL_CONN_NONE,
+ udp4Table->table[i].dwOwningPid);
+ if (!py_conn_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_conn_tuple))
+ goto error;
+ Py_CLEAR(py_conn_tuple);
+ }
+
+ free(table);
+ table = NULL;
+ }
+
+ // UDP IPv6
+
+ if ((PySequence_Contains(py_af_filter, _AF_INET6) == 1) &&
+ (PySequence_Contains(py_type_filter, _SOCK_DGRAM) == 1) &&
+ (RtlIpv6AddressToStringA != NULL))
+ {
+ table = NULL;
+ py_conn_tuple = NULL;
+ py_addr_tuple_local = NULL;
+ py_addr_tuple_remote = NULL;
+ table = __GetExtendedUdpTable(AF_INET6);
+ if (table == NULL)
+ goto error;
+ udp6Table = table;
+ for (i = 0; i < udp6Table->dwNumEntries; i++) {
+ if (pid != -1) {
+ if (udp6Table->table[i].dwOwningPid != pid) {
+ continue;
+ }
+ }
+
+ if (memcmp(udp6Table->table[i].ucLocalAddr, null_address, 16)
+ != 0 || udp6Table->table[i].dwLocalPort != 0)
+ {
+ struct in6_addr addr;
+
+ memcpy(&addr, udp6Table->table[i].ucLocalAddr, 16);
+ RtlIpv6AddressToStringA(&addr, addressBufferLocal);
+ py_addr_tuple_local = Py_BuildValue(
+ "(si)",
+ addressBufferLocal,
+ BYTESWAP_USHORT(udp6Table->table[i].dwLocalPort));
+ }
+ else {
+ py_addr_tuple_local = PyTuple_New(0);
+ }
+
+ if (py_addr_tuple_local == NULL)
+ goto error;
+
+ py_conn_tuple = Py_BuildValue(
+ "(iiiNNiI)",
+ -1,
+ AF_INET6,
+ SOCK_DGRAM,
+ py_addr_tuple_local,
+ PyTuple_New(0),
+ PSUTIL_CONN_NONE,
+ udp6Table->table[i].dwOwningPid);
+ if (!py_conn_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_conn_tuple))
+ goto error;
+ Py_CLEAR(py_conn_tuple);
+ }
+
+ free(table);
+ table = NULL;
+ }
+
+ psutil_conn_decref_objs();
+ return py_retlist;
+
+error:
+ psutil_conn_decref_objs();
+ Py_XDECREF(py_conn_tuple);
+ Py_XDECREF(py_addr_tuple_local);
+ Py_XDECREF(py_addr_tuple_remote);
+ Py_DECREF(py_retlist);
+ if (table != NULL)
+ free(table);
+ return NULL;
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/socks.h b/contrib/python/psutil/py3/psutil/arch/windows/socks.h
new file mode 100644
index 0000000000..cd9ba58dcb
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/socks.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola', Jeff Tang. 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_connections(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
new file mode 100644
index 0000000000..f9a847d3bf
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/wmi.c
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ *
+ * Functions related to the Windows Management Instrumentation API.
+ */
+
+#include <Python.h>
+#include <windows.h>
+#include <pdh.h>
+
+#include "../../_psutil_common.h"
+
+
+// We use an exponentially weighted moving average, just like Unix systems do
+// https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation
+//
+// These constants serve as the damping factor and are calculated with
+// 1 / exp(sampling interval in seconds / window size in seconds)
+//
+// This formula comes from linux's include/linux/sched/loadavg.h
+// https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23
+#define LOADAVG_FACTOR_1F 0.9200444146293232478931553241
+#define LOADAVG_FACTOR_5F 0.9834714538216174894737477501
+#define LOADAVG_FACTOR_15F 0.9944598480048967508795473394
+// The time interval in seconds between taking load counts, same as Linux
+#define SAMPLING_INTERVAL 5
+
+double load_avg_1m = 0;
+double load_avg_5m = 0;
+double load_avg_15m = 0;
+
+
+VOID CALLBACK LoadAvgCallback(PVOID hCounter, BOOLEAN timedOut) {
+ PDH_FMT_COUNTERVALUE displayValue;
+ double currentLoad;
+ PDH_STATUS err;
+
+ err = PdhGetFormattedCounterValue(
+ (PDH_HCOUNTER)hCounter, PDH_FMT_DOUBLE, 0, &displayValue);
+ // Skip updating the load if we can't get the value successfully
+ if (err != ERROR_SUCCESS) {
+ return;
+ }
+ currentLoad = displayValue.doubleValue;
+
+ load_avg_1m = load_avg_1m * LOADAVG_FACTOR_1F + currentLoad * \
+ (1.0 - LOADAVG_FACTOR_1F);
+ load_avg_5m = load_avg_5m * LOADAVG_FACTOR_5F + currentLoad * \
+ (1.0 - LOADAVG_FACTOR_5F);
+ load_avg_15m = load_avg_15m * LOADAVG_FACTOR_15F + currentLoad * \
+ (1.0 - LOADAVG_FACTOR_15F);
+}
+
+
+PyObject *
+psutil_init_loadavg_counter(PyObject *self, PyObject *args) {
+ WCHAR *szCounterPath = L"\\System\\Processor Queue Length";
+ PDH_STATUS s;
+ BOOL ret;
+ HQUERY hQuery;
+ HCOUNTER hCounter;
+ HANDLE event;
+ HANDLE waitHandle;
+
+ if ((PdhOpenQueryW(NULL, 0, &hQuery)) != ERROR_SUCCESS)
+ goto error;
+
+ s = PdhAddEnglishCounterW(hQuery, szCounterPath, 0, &hCounter);
+ if (s != ERROR_SUCCESS)
+ goto error;
+
+ event = CreateEventW(NULL, FALSE, FALSE, L"LoadUpdateEvent");
+ if (event == NULL) {
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ }
+
+ s = PdhCollectQueryDataEx(hQuery, SAMPLING_INTERVAL, event);
+ if (s != ERROR_SUCCESS)
+ goto error;
+
+ ret = RegisterWaitForSingleObject(
+ &waitHandle,
+ event,
+ (WAITORTIMERCALLBACK)LoadAvgCallback,
+ (PVOID)
+ hCounter,
+ INFINITE,
+ WT_EXECUTEDEFAULT);
+
+ if (ret == 0) {
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+
+error:
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+}
+
+
+/*
+ * Gets the emulated 1 minute, 5 minute and 15 minute load averages
+ * (processor queue length) for the system.
+ * `init_loadavg_counter` must be called before this function to engage the
+ * mechanism that records load values.
+ */
+PyObject *
+psutil_get_loadavg(PyObject *self, PyObject *args) {
+ return Py_BuildValue("(ddd)", load_avg_1m, load_avg_5m, load_avg_15m);
+}
diff --git a/contrib/python/psutil/py3/psutil/arch/windows/wmi.h b/contrib/python/psutil/py3/psutil/arch/windows/wmi.h
new file mode 100644
index 0000000000..311242a393
--- /dev/null
+++ b/contrib/python/psutil/py3/psutil/arch/windows/wmi.h
@@ -0,0 +1,10 @@
+/*
+ * 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_init_loadavg_counter();
+PyObject* psutil_get_loadavg();
diff --git a/contrib/python/psutil/py3/test/test.py b/contrib/python/psutil/py3/test/test.py
new file mode 100644
index 0000000000..4f5a0e50d8
--- /dev/null
+++ b/contrib/python/psutil/py3/test/test.py
@@ -0,0 +1,4 @@
+from library.python.import_test import check_imports
+test_imports = lambda: check_imports(no_check=['psutil._ps*'])
+#from psutil._psutil_posix import net_if_addrs
+
diff --git a/contrib/python/psutil/test/ya.make b/contrib/python/psutil/py3/test/ya.make
index 72d9c04afc..72d9c04afc 100644
--- a/contrib/python/psutil/test/ya.make
+++ b/contrib/python/psutil/py3/test/ya.make
diff --git a/contrib/python/psutil/py3/ya.make b/contrib/python/psutil/py3/ya.make
new file mode 100644
index 0000000000..d90f7e52f2
--- /dev/null
+++ b/contrib/python/psutil/py3/ya.make
@@ -0,0 +1,147 @@
+PY3_LIBRARY()
+
+LICENSE(BSD-3-Clause)
+
+VERSION(5.8.0)
+
+NO_UTIL()
+
+SRCDIR(contrib/python/psutil/py3/psutil)
+
+NO_COMPILER_WARNINGS()
+
+CFLAGS(
+ -DPSUTIL_VERSION=580
+)
+
+IF (OS_LINUX OR OS_DARWIN)
+ CFLAGS(
+ -DPSUTIL_POSIX=1
+ )
+ SRCS(
+ _psutil_common.c
+ _psutil_posix.c
+ )
+ PY_REGISTER(psutil._psutil_posix)
+ENDIF ()
+
+IF (OS_LINUX)
+ CFLAGS(
+ -DPSUTIL_LINUX=1
+ )
+
+ SRCS(
+ _psutil_linux.c
+ )
+ PY_REGISTER(psutil._psutil_linux)
+ENDIF ()
+
+IF (OS_DARWIN)
+ CFLAGS(
+ -DPSUTIL_OSX=1
+ )
+
+ EXTRALIBS("-framework CoreFoundation -framework IOKit")
+
+ PEERDIR(
+ contrib/python/psutil/py3/psutil/arch/osx
+ )
+
+ SRCS(
+ _psutil_osx.c
+ )
+
+ PY_REGISTER(psutil._psutil_osx)
+ENDIF ()
+
+IF (OS_WINDOWS)
+ CFLAGS(
+ -DPSUTIL_WINDOWS=1
+ -DPSUTIL_SIZEOF_PID_T=4
+ )
+
+ LDFLAGS(
+ Shell32.lib
+ PowrProf.lib
+ Wtsapi32.lib
+ Pdh.lib
+ )
+
+ SRCS(
+ _psutil_common.c
+ _psutil_windows.c
+ arch/windows/cpu.c
+ arch/windows/disk.c
+ arch/windows/net.c
+ arch/windows/process_handles.c
+ arch/windows/process_info.c
+ arch/windows/process_utils.c
+ arch/windows/security.c
+ arch/windows/services.c
+ arch/windows/socks.c
+ arch/windows/wmi.c
+ )
+
+ PY_REGISTER(psutil._psutil_windows)
+ENDIF ()
+
+NO_CHECK_IMPORTS(
+ psutil._psbsd
+ psutil._psosx
+ psutil._pssunos
+ psutil._psutil_bsd
+ psutil._psutil_common
+ psutil._psutil_osx
+ psutil._psutil_sunos
+ psutil._psutil_windows
+ psutil._pswindows
+)
+
+PY_SRCS(
+ TOP_LEVEL
+ psutil/__init__.py
+ psutil/_common.py
+ psutil/_compat.py
+)
+
+IF (OS_LINUX OR OS_DARWIN)
+ PY_SRCS(
+ TOP_LEVEL
+ psutil/_psposix.py
+ )
+ENDIF ()
+
+IF (OS_LINUX)
+ PY_SRCS(
+ TOP_LEVEL
+ psutil/_pslinux.py
+ )
+ENDIF ()
+
+IF (OS_DARWIN)
+ PY_SRCS(
+ TOP_LEVEL
+ psutil/_psosx.py
+ )
+ENDIF ()
+
+IF (OS_WINDOWS)
+ PY_SRCS(
+ TOP_LEVEL
+ psutil/_pswindows.py
+ )
+ENDIF ()
+
+RESOURCE_FILES(
+ PREFIX contrib/python/psutil/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+NO_LINT()
+
+END()
+
+RECURSE_FOR_TESTS(
+ test
+)
diff --git a/contrib/python/psutil/ya.make b/contrib/python/psutil/ya.make
index 405c886a3c..f5946cffcc 100644
--- a/contrib/python/psutil/ya.make
+++ b/contrib/python/psutil/ya.make
@@ -1,147 +1,18 @@
PY23_LIBRARY()
-LICENSE(BSD-3-Clause)
+LICENSE(Service-Py23-Proxy)
-VERSION(5.8.0)
-
-NO_UTIL()
-
-SRCDIR(contrib/python/psutil/psutil)
-
-NO_COMPILER_WARNINGS()
-
-CFLAGS(
- -DPSUTIL_VERSION=580
-)
-
-IF (OS_LINUX OR OS_DARWIN)
- CFLAGS(
- -DPSUTIL_POSIX=1
- )
- SRCS(
- _psutil_common.c
- _psutil_posix.c
- )
- PY_REGISTER(psutil._psutil_posix)
-ENDIF ()
-
-IF (OS_LINUX)
- CFLAGS(
- -DPSUTIL_LINUX=1
- )
-
- SRCS(
- _psutil_linux.c
- )
- PY_REGISTER(psutil._psutil_linux)
-ENDIF ()
-
-IF (OS_DARWIN)
- CFLAGS(
- -DPSUTIL_OSX=1
- )
-
- EXTRALIBS("-framework CoreFoundation -framework IOKit")
-
- PEERDIR(
- contrib/python/psutil/psutil/arch/osx
- )
-
- SRCS(
- _psutil_osx.c
- )
-
- PY_REGISTER(psutil._psutil_osx)
-ENDIF ()
-
-IF (OS_WINDOWS)
- CFLAGS(
- -DPSUTIL_WINDOWS=1
- -DPSUTIL_SIZEOF_PID_T=4
- )
-
- LDFLAGS(
- Shell32.lib
- PowrProf.lib
- Wtsapi32.lib
- Pdh.lib
- )
-
- SRCS(
- _psutil_common.c
- _psutil_windows.c
- arch/windows/cpu.c
- arch/windows/disk.c
- arch/windows/net.c
- arch/windows/process_handles.c
- arch/windows/process_info.c
- arch/windows/process_utils.c
- arch/windows/security.c
- arch/windows/services.c
- arch/windows/socks.c
- arch/windows/wmi.c
- )
-
- PY_REGISTER(psutil._psutil_windows)
-ENDIF ()
-
-NO_CHECK_IMPORTS(
- psutil._psbsd
- psutil._psosx
- psutil._pssunos
- psutil._psutil_bsd
- psutil._psutil_common
- psutil._psutil_osx
- psutil._psutil_sunos
- psutil._psutil_windows
- psutil._pswindows
-)
-
-PY_SRCS(
- TOP_LEVEL
- psutil/__init__.py
- psutil/_common.py
- psutil/_compat.py
-)
-
-IF (OS_LINUX OR OS_DARWIN)
- PY_SRCS(
- TOP_LEVEL
- psutil/_psposix.py
- )
-ENDIF ()
-
-IF (OS_LINUX)
- PY_SRCS(
- TOP_LEVEL
- psutil/_pslinux.py
- )
-ENDIF ()
-
-IF (OS_DARWIN)
- PY_SRCS(
- TOP_LEVEL
- psutil/_psosx.py
- )
-ENDIF ()
-
-IF (OS_WINDOWS)
- PY_SRCS(
- TOP_LEVEL
- psutil/_pswindows.py
- )
-ENDIF ()
-
-RESOURCE_FILES(
- PREFIX contrib/python/psutil/
- .dist-info/METADATA
- .dist-info/top_level.txt
-)
+IF (PYTHON2)
+ PEERDIR(contrib/python/psutil/py2)
+ELSE()
+ PEERDIR(contrib/python/psutil/py3)
+ENDIF()
NO_LINT()
END()
-RECURSE_FOR_TESTS(
- test
+RECURSE(
+ py2
+ py3
)