summaryrefslogtreecommitdiff
path: root/psutil
diff options
context:
space:
mode:
Diffstat (limited to 'psutil')
-rw-r--r--psutil/DEVNOTES5
-rw-r--r--psutil/__init__.py299
-rw-r--r--psutil/_common.py5
-rw-r--r--psutil/_exceptions.py94
-rw-r--r--psutil/_psaix.py573
-rw-r--r--psutil/_psbsd.py33
-rw-r--r--psutil/_pslinux.py82
-rw-r--r--psutil/_psosx.py54
-rw-r--r--psutil/_psposix.py12
-rw-r--r--psutil/_pssunos.py20
-rw-r--r--psutil/_psutil_aix.c988
-rw-r--r--psutil/_psutil_bsd.c8
-rw-r--r--psutil/_psutil_common.c99
-rw-r--r--psutil/_psutil_common.h14
-rw-r--r--psutil/_psutil_linux.c8
-rw-r--r--psutil/_psutil_osx.c144
-rw-r--r--psutil/_psutil_posix.c22
-rw-r--r--psutil/_psutil_sunos.c50
-rw-r--r--psutil/_psutil_windows.c196
-rw-r--r--psutil/_pswindows.py11
-rw-r--r--psutil/arch/aix/common.c79
-rw-r--r--psutil/arch/aix/common.h31
-rw-r--r--psutil/arch/aix/ifaddrs.c149
-rw-r--r--psutil/arch/aix/ifaddrs.h35
-rw-r--r--psutil/arch/aix/net_connections.c287
-rw-r--r--psutil/arch/aix/net_connections.h15
-rw-r--r--psutil/arch/aix/net_kernel_structs.h110
-rw-r--r--psutil/arch/freebsd/proc_socks.c38
-rw-r--r--psutil/arch/freebsd/specific.c14
-rw-r--r--psutil/arch/netbsd/specific.c10
-rw-r--r--psutil/arch/openbsd/specific.c10
-rw-r--r--psutil/arch/osx/process_info.c8
-rw-r--r--psutil/arch/solaris/v10/ifaddrs.h4
-rw-r--r--psutil/arch/windows/ntextapi.h12
-rw-r--r--psutil/arch/windows/process_info.c10
-rw-r--r--psutil/tests/__init__.py68
-rwxr-xr-xpsutil/tests/__main__.py12
-rwxr-xr-xpsutil/tests/test_aix.py121
-rwxr-xr-xpsutil/tests/test_connections.py4
-rwxr-xr-xpsutil/tests/test_contracts.py44
-rwxr-xr-xpsutil/tests/test_linux.py46
-rwxr-xr-xpsutil/tests/test_misc.py27
-rwxr-xr-xpsutil/tests/test_osx.py13
-rwxr-xr-xpsutil/tests/test_posix.py62
-rwxr-xr-xpsutil/tests/test_process.py149
-rwxr-xr-xpsutil/tests/test_system.py8
-rwxr-xr-xpsutil/tests/test_unicode.py41
47 files changed, 3524 insertions, 600 deletions
diff --git a/psutil/DEVNOTES b/psutil/DEVNOTES
new file mode 100644
index 00000000..4fd15ea3
--- /dev/null
+++ b/psutil/DEVNOTES
@@ -0,0 +1,5 @@
+API REFERENCES
+==============
+
+- psutil.sensors_battery:
+ https://github.com/Kentzo/Power/
diff --git a/psutil/__init__.py b/psutil/__init__.py
index 9c664a0c..8f929846 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -11,10 +11,11 @@ sensors) in Python. Supported platforms:
- Linux
- Windows
- OSX
- - Sun Solaris
- FreeBSD
- OpenBSD
- NetBSD
+ - Sun Solaris
+ - AIX
Works with Python versions from 2.6 to 3.X.
"""
@@ -23,6 +24,7 @@ from __future__ import division
import collections
import contextlib
+import datetime
import errno
import functools
import os
@@ -73,6 +75,7 @@ from ._common import NIC_DUPLEX_FULL
from ._common import NIC_DUPLEX_HALF
from ._common import NIC_DUPLEX_UNKNOWN
+from ._common import AIX
from ._common import BSD
from ._common import FREEBSD # NOQA
from ._common import LINUX
@@ -83,6 +86,12 @@ from ._common import POSIX # NOQA
from ._common import SUNOS
from ._common import WINDOWS
+from ._exceptions import AccessDenied
+from ._exceptions import Error
+from ._exceptions import NoSuchProcess
+from ._exceptions import TimeoutExpired
+from ._exceptions import ZombieProcess
+
if LINUX:
# This is public API and it will be retrieved from _pslinux.py
# via sys.modules.
@@ -158,6 +167,13 @@ elif SUNOS:
# _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)
@@ -185,7 +201,7 @@ __all__ = [
"POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED",
"BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "OSX", "POSIX", "SUNOS",
- "WINDOWS",
+ "WINDOWS", "AIX",
# classes
"Process", "Popen",
@@ -203,7 +219,7 @@ __all__ = [
]
__all__.extend(_psplatform.__extra__all__)
__author__ = "Giampaolo Rodola'"
-__version__ = "5.3.0"
+__version__ = "5.4.3"
version_info = tuple([int(num) for num in __version__.split('.')])
AF_LINK = _psplatform.AF_LINK
POWER_TIME_UNLIMITED = _common.POWER_TIME_UNLIMITED
@@ -235,112 +251,29 @@ if (int(__version__.replace('.', '')) !=
# =====================================================================
-# --- exceptions
+# --- Utils
# =====================================================================
-class Error(Exception):
- """Base exception class. All other psutil exceptions inherit
- from this one.
- """
-
- def __init__(self, msg=""):
- Exception.__init__(self, msg)
- self.msg = msg
-
- def __repr__(self):
- ret = "%s.%s %s" % (self.__class__.__module__,
- 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.
- """
-
- 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 OSX, 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.
- """
-
- 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."""
-
- 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
+if hasattr(_psplatform, 'ppid_map'):
+ # Faster version (Windows and Linux).
+ _ppid_map = _psplatform.ppid_map
+else:
+ 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:
+ proc = _psplatform.Process(pid)
+ ppid = proc.ppid()
+ except (NoSuchProcess, AccessDenied):
+ # Note: AccessDenied is unlikely to happen.
+ pass
else:
- self.msg = ""
-
-
-class TimeoutExpired(Error):
- """Raised on Process.wait(timeout) if timeout expires and process
- is still alive.
- """
-
- 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
-
-
-# push exception classes into platform specific module namespace
-_psplatform.NoSuchProcess = NoSuchProcess
-_psplatform.ZombieProcess = ZombieProcess
-_psplatform.AccessDenied = AccessDenied
-_psplatform.TimeoutExpired = TimeoutExpired
-
-
-# =====================================================================
-# --- Process class
-# =====================================================================
+ ret[pid] = ppid
+ return ret
def _assert_pid_not_reused(fun):
@@ -355,6 +288,22 @@ def _assert_pid_not_reused(fun):
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.
@@ -440,21 +389,26 @@ class Process(object):
def __str__(self):
try:
- pid = self.pid
- name = repr(self.name())
+ info = collections.OrderedDict()
+ except AttributeError:
+ info = {} # Python 2.6
+ info["pid"] = self.pid
+ try:
+ info["name"] = self.name()
+ if self._create_time:
+ info['started'] = _pprint_secs(self._create_time)
except ZombieProcess:
- details = "(pid=%s (zombie))" % self.pid
+ info["status"] = "zombie"
except NoSuchProcess:
- details = "(pid=%s (terminated))" % self.pid
+ info["status"] = "terminated"
except AccessDenied:
- details = "(pid=%s)" % (self.pid)
- else:
- details = "(pid=%s, name=%s)" % (pid, name)
- return "%s.%s%s" % (self.__class__.__module__,
- self.__class__.__name__, details)
+ pass
+ return "%s.%s(%s)" % (
+ self.__class__.__module__,
+ self.__class__.__name__,
+ ", ".join(["%s=%r" % (k, v) for k, v in info.items()]))
- def __repr__(self):
- return "<%s at %s>" % (self.__str__(), id(self))
+ __repr__ = __str__
def __eq__(self, other):
# Test for equality with another Process object based
@@ -785,7 +739,7 @@ class Process(object):
"""
return self._proc.num_fds()
- # Linux, BSD and Windows only
+ # Linux, BSD, AIX and Windows only
if hasattr(_psplatform.Process, "io_counters"):
def io_counters(self):
@@ -908,13 +862,15 @@ class Process(object):
"""Return the number of threads used by this process."""
return self._proc.num_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()
+ 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):
@@ -943,73 +899,47 @@ class Process(object):
process Y won't be listed as the reference to process A
is lost.
"""
- if hasattr(_psplatform, 'ppid_map'):
- # Windows only: obtain a {pid:ppid, ...} dict for all running
- # processes in one shot (faster).
- ppid_map = _psplatform.ppid_map()
- else:
- ppid_map = None
-
+ ppid_map = _ppid_map()
ret = []
if not recursive:
- if ppid_map is None:
- # 'slow' version, common to all platforms except Windows
- for p in process_iter():
+ for pid, ppid in ppid_map.items():
+ if ppid == self.pid:
try:
- if p.ppid() == self.pid:
- # if child happens to be older than its parent
- # (self) it means child's PID has been reused
- if self.create_time() <= p.create_time():
- ret.append(p)
+ 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: # pragma: no cover
- # Windows only (faster)
- 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 dict where 'values' are all the processes
- # having 'key' as their parent
- table = collections.defaultdict(list)
- if ppid_map is None:
- for p in process_iter():
- try:
- table[p.ppid()].append(p)
- except (NoSuchProcess, ZombieProcess):
- pass
- else: # pragma: no cover
- for pid, ppid in ppid_map.items():
- try:
- p = Process(pid)
- table[ppid].append(p)
- except (NoSuchProcess, ZombieProcess):
- pass
- # At this point we have a mapping table where table[self.pid]
- # are the current process' children.
- # Below, we look for all descendants recursively, similarly
- # to a recursive function call.
- checkpids = [self.pid]
- for pid in checkpids:
- for child in table[pid]:
+ # 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()
- except (NoSuchProcess, ZombieProcess):
- pass
- else:
if intime:
ret.append(child)
- if child.pid not in checkpids:
- checkpids.append(child.pid)
+ stack.append(child_pid)
+ except (NoSuchProcess, ZombieProcess):
+ pass
return ret
def cpu_percent(self, interval=None):
@@ -1157,13 +1087,11 @@ class Process(object):
('rss', 'vms', 'shared', 'text', 'lib', 'data', 'dirty', 'uss', 'pss')
"""
valid_types = list(_psplatform.pfullmem._fields)
- if hasattr(_psplatform, "pfullmem"):
- valid_types.extend(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_full_info if memtype in ('uss', 'pss', 'swap') else \
- self.memory_info
+ fun = self.memory_info if memtype in _psplatform.pmem._fields else \
+ self.memory_full_info
metrics = fun()
value = getattr(metrics, memtype)
@@ -1179,7 +1107,6 @@ class Process(object):
if hasattr(_psplatform.Process, "memory_maps"):
# Available everywhere except OpenBSD and NetBSD.
-
def memory_maps(self, grouped=True):
"""Return process' mapped memory regions as a list of namedtuples
whose fields are variable depending on the platform.
@@ -2308,7 +2235,7 @@ if hasattr(_psplatform, "sensors_fans"):
__all__.append("sensors_fans")
-# Linux, Windows, FreeBSD
+# Linux, Windows, FreeBSD, OSX
if hasattr(_psplatform, "sensors_battery"):
def sensors_battery():
@@ -2378,8 +2305,6 @@ def test(): # pragma: no cover
"""List info of all currently running processes emulating ps aux
output.
"""
- import datetime
-
today_day = datetime.date.today()
templ = "%-10s %5s %4s %7s %7s %-13s %5s %7s %s"
attrs = ['pid', 'memory_percent', 'name', 'cpu_times', 'create_time',
diff --git a/psutil/_common.py b/psutil/_common.py
index 7c4af3d8..870971e4 100644
--- a/psutil/_common.py
+++ b/psutil/_common.py
@@ -81,6 +81,7 @@ OPENBSD = sys.platform.startswith("openbsd")
NETBSD = sys.platform.startswith("netbsd")
BSD = FREEBSD or OPENBSD or NETBSD
SUNOS = sys.platform.startswith("sunos") or sys.platform.startswith("solaris")
+AIX = sys.platform.startswith("aix")
# ===================================================================
@@ -460,14 +461,14 @@ def deprecated_method(replacement):
'replcement' is the method name which will be called instead.
"""
def outer(fun):
- msg = "%s() is deprecated; use %s() instead" % (
+ 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)
+ warnings.warn(msg, category=FutureWarning, stacklevel=2)
return getattr(self, replacement)(*args, **kwargs)
return inner
return outer
diff --git a/psutil/_exceptions.py b/psutil/_exceptions.py
new file mode 100644
index 00000000..c08e6d83
--- /dev/null
+++ b/psutil/_exceptions.py
@@ -0,0 +1,94 @@
+# 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.
+
+
+class Error(Exception):
+ """Base exception class. All other psutil exceptions inherit
+ from this one.
+ """
+
+ 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.
+ """
+
+ 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 OSX, 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.
+ """
+
+ 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."""
+
+ 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.
+ """
+
+ 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
diff --git a/psutil/_psaix.py b/psutil/_psaix.py
new file mode 100644
index 00000000..9abc8d17
--- /dev/null
+++ b/psutil/_psaix.py
@@ -0,0 +1,573 @@
+# Copyright (c) 2009, Giampaolo Rodola'
+# 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.
+
+"""AIX platform implementation."""
+
+import errno
+import glob
+import os
+import re
+import subprocess
+import sys
+from collections import namedtuple
+from socket import AF_INET
+
+from . import _common
+from . import _psposix
+from . import _psutil_aix as cext
+from . import _psutil_posix as cext_posix
+from ._common import AF_INET6
+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 sockfam_to_enum
+from ._common import socktype_to_enum
+from ._common import usage_percent
+from ._compat import PY3
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
+
+
+__extra__all__ = ["PROCFS_PATH"]
+
+
+# =====================================================================
+# --- globals
+# =====================================================================
+
+
+HAS_THREADS = hasattr(cext, "proc_threads")
+
+PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
+AF_LINK = cext_posix.AF_LINK
+
+PROC_STATUSES = {
+ cext.SIDL: _common.STATUS_IDLE,
+ cext.SZOMB: _common.STATUS_ZOMBIE,
+ cext.SACTIVE: _common.STATUS_RUNNING,
+ cext.SSWAP: _common.STATUS_RUNNING, # TODO what status is this?
+ cext.SSTOP: _common.STATUS_STOPPED,
+}
+
+TCP_STATUSES = {
+ cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
+ cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
+ cext.TCPS_SYN_RCVD: _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_info_map = dict(
+ ppid=0,
+ rss=1,
+ vms=2,
+ create_time=3,
+ nice=4,
+ num_threads=5,
+ status=6,
+ ttynr=7)
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.Process.memory_info()
+pmem = namedtuple('pmem', ['rss', 'vms'])
+# psutil.Process.memory_full_info()
+pfullmem = pmem
+# psutil.Process.cpu_times()
+scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
+# psutil.virtual_memory()
+svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
+# psutil.Process.memory_maps(grouped=True)
+pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss', 'anon', 'locked'])
+# psutil.Process.memory_maps(grouped=False)
+pmmap_ext = namedtuple(
+ 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
+
+
+# =====================================================================
+# --- utils
+# =====================================================================
+
+
+def get_procfs_path():
+ """Return updated psutil.PROCFS_PATH constant."""
+ return sys.modules['psutil'].PROCFS_PATH
+
+
+# =====================================================================
+# --- memory
+# =====================================================================
+
+
+def virtual_memory():
+ total, avail, free, pinned, inuse = cext.virtual_mem()
+ percent = usage_percent((total - avail), total, _round=1)
+ return svmem(total, avail, percent, inuse, free)
+
+
+def swap_memory():
+ """Swap system memory as a (total, used, free, sin, sout) tuple."""
+ total, free, sin, sout = cext.swap_mem()
+ used = total - free
+ percent = usage_percent(used, total, _round=1)
+ return _common.sswap(total, used, free, percent, sin, sout)
+
+
+# =====================================================================
+# --- CPU
+# =====================================================================
+
+
+def cpu_times():
+ """Return system-wide CPU times as a named tuple"""
+ ret = cext.per_cpu_times()
+ return scputimes(*[sum(x) for x in zip(*ret)])
+
+
+def per_cpu_times():
+ """Return system per-CPU times as a list of named tuples"""
+ ret = cext.per_cpu_times()
+ return [scputimes(*x) for x in ret]
+
+
+def cpu_count_logical():
+ """Return the number of logical CPUs in the system."""
+ try:
+ return os.sysconf("SC_NPROCESSORS_ONLN")
+ except ValueError:
+ # mimic os.cpu_count() behavior
+ return None
+
+
+def cpu_count_physical():
+ cmd = "lsdev -Cc processor"
+ p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if PY3:
+ stdout, stderr = [x.decode(sys.stdout.encoding)
+ for x in (stdout, stderr)]
+ if p.returncode != 0:
+ raise RuntimeError("%r command error\n%s" % (cmd, stderr))
+ processors = stdout.strip().splitlines()
+ return len(processors) or None
+
+
+def cpu_stats():
+ """Return various CPU stats as a named tuple."""
+ ctx_switches, interrupts, soft_interrupts, syscalls = cext.cpu_stats()
+ return _common.scpustats(
+ ctx_switches, interrupts, soft_interrupts, syscalls)
+
+
+# =====================================================================
+# --- disks
+# =====================================================================
+
+
+disk_io_counters = cext.disk_io_counters
+disk_usage = _psposix.disk_usage
+
+
+def disk_partitions(all=False):
+ """Return system disk partitions."""
+ # TODO - the filtering logic should be better checked so that
+ # it tries to reflect 'df' as much as possible
+ retlist = []
+ partitions = cext.disk_partitions()
+ for partition in partitions:
+ device, mountpoint, fstype, opts = partition
+ if device == 'none':
+ device = ''
+ if not all:
+ # Differently from, say, Linux, we don't have a list of
+ # common fs types so the best we can do, AFAIK, is to
+ # filter by filesystem having a total size > 0.
+ if not disk_usage(mountpoint).total:
+ continue
+ ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
+ retlist.append(ntuple)
+ return retlist
+
+
+# =====================================================================
+# --- network
+# =====================================================================
+
+
+net_if_addrs = cext_posix.net_if_addrs
+net_io_counters = cext.net_io_counters
+
+
+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).
+ """
+ cmap = _common.conn_tmap
+ if kind not in cmap:
+ raise ValueError("invalid %r kind argument; choose between %s"
+ % (kind, ', '.join([repr(x) for x in cmap])))
+ families, types = _common.conn_tmap[kind]
+ rawlist = cext.net_connections(_pid)
+ ret = set()
+ for item in rawlist:
+ fd, fam, type_, laddr, raddr, status, pid = item
+ if fam not in families:
+ continue
+ if type_ not in types:
+ continue
+ status = TCP_STATUSES[status]
+ if fam in (AF_INET, AF_INET6):
+ if laddr:
+ laddr = _common.addr(*laddr)
+ if raddr:
+ raddr = _common.addr(*raddr)
+ fam = sockfam_to_enum(fam)
+ type_ = socktype_to_enum(type_)
+ if _pid == -1:
+ nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid)
+ else:
+ nt = _common.pconn(fd, fam, type_, laddr, raddr, status)
+ ret.add(nt)
+ return list(ret)
+
+
+def net_if_stats():
+ """Get NIC stats (isup, duplex, speed, mtu)."""
+ duplex_map = {"Full": NIC_DUPLEX_FULL,
+ "Half": NIC_DUPLEX_HALF}
+ names = set([x[0] for x in net_if_addrs()])
+ ret = {}
+ for name in names:
+ isup, mtu = cext.net_if_stats(name)
+
+ # try to get speed and duplex
+ # TODO: rewrite this in C (entstat forks, so use truss -f to follow.
+ # looks like it is using an undocumented ioctl?)
+ duplex = ""
+ speed = 0
+ p = subprocess.Popen(["/usr/bin/entstat", "-d", name],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if PY3:
+ stdout, stderr = [x.decode(sys.stdout.encoding)
+ for x in (stdout, stderr)]
+ if p.returncode == 0:
+ re_result = re.search("Running: (\d+) Mbps.*?(\w+) Duplex", stdout)
+ if re_result is not None:
+ speed = int(re_result.group(1))
+ duplex = re_result.group(2)
+
+ duplex = duplex_map.get(duplex, NIC_DUPLEX_UNKNOWN)
+ 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()
+ localhost = (':0.0', ':0')
+ 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 localhost:
+ hostname = 'localhost'
+ nt = _common.suser(user, tty, hostname, tstamp, pid)
+ retlist.append(nt)
+ return retlist
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+def pids():
+ """Returns a list of PIDs currently running on the system."""
+ return [int(x) for x in os.listdir(get_procfs_path()) if x.isdigit()]
+
+
+def pid_exists(pid):
+ """Check for the existence of a unix pid."""
+ return os.path.exists(os.path.join(get_procfs_path(), str(pid), "psinfo"))
+
+
+def wrap_exceptions(fun):
+ """Call callable into a try/except clause and translate ENOENT,
+ EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
+ """
+
+ def wrapper(self, *args, **kwargs):
+ try:
+ return fun(self, *args, **kwargs)
+ except EnvironmentError as err:
+ # support for private module import
+ if (NoSuchProcess is None or AccessDenied is None or
+ ZombieProcess is None):
+ raise
+ # ENOENT (no such file or directory) gets raised on open().
+ # ESRCH (no such process) can get raised on read() if
+ # process is gone in meantime.
+ if err.errno in (errno.ENOENT, errno.ESRCH):
+ if not pid_exists(self.pid):
+ raise NoSuchProcess(self.pid, self._name)
+ else:
+ raise ZombieProcess(self.pid, self._name, self._ppid)
+ if err.errno in (errno.EPERM, errno.EACCES):
+ raise AccessDenied(self.pid, self._name)
+ raise
+ return wrapper
+
+
+class Process(object):
+ """Wrapper class around underlying C implementation."""
+
+ __slots__ = ["pid", "_name", "_ppid", "_procfs_path"]
+
+ def __init__(self, pid):
+ self.pid = pid
+ self._name = None
+ self._ppid = None
+ self._procfs_path = get_procfs_path()
+
+ def oneshot_enter(self):
+ self._proc_name_and_args.cache_activate()
+ self._proc_basic_info.cache_activate()
+ self._proc_cred.cache_activate()
+
+ def oneshot_exit(self):
+ self._proc_name_and_args.cache_deactivate()
+ self._proc_basic_info.cache_deactivate()
+ self._proc_cred.cache_deactivate()
+
+ @memoize_when_activated
+ def _proc_name_and_args(self):
+ return cext.proc_name_and_args(self.pid, self._procfs_path)
+
+ @memoize_when_activated
+ def _proc_basic_info(self):
+ return cext.proc_basic_info(self.pid, self._procfs_path)
+
+ @memoize_when_activated
+ def _proc_cred(self):
+ return cext.proc_cred(self.pid, self._procfs_path)
+
+ @wrap_exceptions
+ def name(self):
+ if self.pid == 0:
+ return "swapper"
+ # note: this is limited to 15 characters
+ return self._proc_name_and_args()[0].rstrip("\x00")
+
+ @wrap_exceptions
+ def exe(self):
+ # there is no way to get executable path in AIX other than to guess,
+ # and guessing is more complex than what's in the wrapping class
+ exe = self.cmdline()[0]
+ if os.path.sep in exe:
+ # relative or absolute path
+ if not os.path.isabs(exe):
+ # if cwd has changed, we're out of luck - this may be wrong!
+ exe = os.path.abspath(os.path.join(self.cwd(), exe))
+ if (os.path.isabs(exe) and
+ os.path.isfile(exe) and
+ os.access(exe, os.X_OK)):
+ return exe
+ # not found, move to search in PATH using basename only
+ exe = os.path.basename(exe)
+ # search for exe name PATH
+ for path in os.environ["PATH"].split(":"):
+ possible_exe = os.path.abspath(os.path.join(path, exe))
+ if (os.path.isfile(possible_exe) and
+ os.access(possible_exe, os.X_OK)):
+ return possible_exe
+ return ''
+
+ @wrap_exceptions
+ def cmdline(self):
+ return self._proc_name_and_args()[1].split(' ')
+
+ @wrap_exceptions
+ def create_time(self):
+ return self._proc_basic_info()[proc_info_map['create_time']]
+
+ @wrap_exceptions
+ def num_threads(self):
+ return self._proc_basic_info()[proc_info_map['num_threads']]
+
+ if HAS_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)
+ # The underlying C implementation retrieves all OS threads
+ # and filters them by PID. At this point we can't tell whether
+ # an empty list means there were no connections for process or
+ # process is no longer active so we force NSP in case the PID
+ # is no longer there.
+ if not retlist:
+ # will raise NSP if process is gone
+ os.stat('%s/%s' % (self._procfs_path, self.pid))
+ return retlist
+
+ @wrap_exceptions
+ def connections(self, kind='inet'):
+ ret = net_connections(kind, _pid=self.pid)
+ # The underlying C implementation retrieves all OS connections
+ # and filters them by PID. At this point we can't tell whether
+ # an empty list means there were no connections for process or
+ # process is no longer active so we force NSP in case the PID
+ # is no longer there.
+ if not ret:
+ # will raise NSP if process is gone
+ os.stat('%s/%s' % (self._procfs_path, self.pid))
+ return ret
+
+ @wrap_exceptions
+ def nice_get(self):
+ return cext_posix.getpriority(self.pid)
+
+ @wrap_exceptions
+ def nice_set(self, value):
+ return cext_posix.setpriority(self.pid, value)
+
+ @wrap_exceptions
+ def ppid(self):
+ self._ppid = self._proc_basic_info()[proc_info_map['ppid']]
+ return self._ppid
+
+ @wrap_exceptions
+ def uids(self):
+ real, effective, saved, _, _, _ = self._proc_cred()
+ return _common.puids(real, effective, saved)
+
+ @wrap_exceptions
+ def gids(self):
+ _, _, _, real, effective, saved = self._proc_cred()
+ return _common.puids(real, effective, saved)
+
+ @wrap_exceptions
+ def cpu_times(self):
+ cpu_times = cext.proc_cpu_times(self.pid, self._procfs_path)
+ return _common.pcputimes(*cpu_times)
+
+ @wrap_exceptions
+ def terminal(self):
+ ttydev = self._proc_basic_info()[proc_info_map['ttynr']]
+ # convert from 64-bit dev_t to 32-bit dev_t and then map the device
+ ttydev = (((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF))
+ # try to match rdev of /dev/pts/* files ttydev
+ for dev in glob.glob("/dev/**/*"):
+ if os.stat(dev).st_rdev == ttydev:
+ return dev
+ return None
+
+ @wrap_exceptions
+ def cwd(self):
+ procfs_path = self._procfs_path
+ try:
+ result = os.readlink("%s/%s/cwd" % (procfs_path, self.pid))
+ return result.rstrip('/')
+ except OSError as err:
+ if err.errno == errno.ENOENT:
+ os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD
+ return None
+ raise
+
+ @wrap_exceptions
+ def memory_info(self):
+ ret = self._proc_basic_info()
+ rss = ret[proc_info_map['rss']] * 1024
+ vms = ret[proc_info_map['vms']] * 1024
+ return pmem(rss, vms)
+
+ memory_full_info = memory_info
+
+ @wrap_exceptions
+ def status(self):
+ code = self._proc_basic_info()[proc_info_map['status']]
+ # XXX is '?' legit? (we're not supposed to return it anyway)
+ return PROC_STATUSES.get(code, '?')
+
+ def open_files(self):
+ # TODO rewrite without using procfiles (stat /proc/pid/fd/* and then
+ # find matching name of the inode)
+ p = subprocess.Popen(["/usr/bin/procfiles", "-n", str(self.pid)],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if PY3:
+ stdout, stderr = [x.decode(sys.stdout.encoding)
+ for x in (stdout, stderr)]
+ if "no such process" in stderr.lower():
+ raise NoSuchProcess(self.pid, self._name)
+ procfiles = re.findall("(\d+): S_IFREG.*\s*.*name:(.*)\n", stdout)
+ retlist = []
+ for fd, path in procfiles:
+ path = path.strip()
+ if path.startswith("//"):
+ path = path[1:]
+ if path.lower() == "cannot be retrieved":
+ continue
+ retlist.append(_common.popenfile(path, int(fd)))
+ return retlist
+
+ @wrap_exceptions
+ def num_fds(self):
+ if self.pid == 0: # no /proc/0/fd
+ return 0
+ return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid)))
+
+ @wrap_exceptions
+ def num_ctx_switches(self):
+ return _common.pctxsw(
+ *cext.proc_num_ctx_switches(self.pid))
+
+ @wrap_exceptions
+ def wait(self, timeout=None):
+ return _psposix.wait_pid(self.pid, timeout, self._name)
+
+ @wrap_exceptions
+ def io_counters(self):
+ try:
+ rc, wc, rb, wb = cext.proc_io_counters(self.pid)
+ except OSError:
+ # if process is terminated, proc_io_counters returns OSError
+ # instead of NSP
+ if not pid_exists(self.pid):
+ raise NoSuchProcess(self.pid, self._name)
+ raise
+ return _common.pio(rc, wc, rb, wb)
diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py
index da264ef3..c2c926e2 100644
--- a/psutil/_psbsd.py
+++ b/psutil/_psbsd.py
@@ -27,6 +27,9 @@ from ._common import sockfam_to_enum
from ._common import socktype_to_enum
from ._common import usage_percent
from ._compat import which
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
__extra__all__ = []
@@ -128,12 +131,6 @@ kinfo_proc_map = dict(
name=24,
)
-# these get overwritten on "import psutil" from the __init__.py file
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
-
# =====================================================================
# --- named tuples
@@ -394,9 +391,12 @@ def net_connections(kind):
# have a very short lifetime so maybe the kernel
# can't initialize their status?
status = TCP_STATUSES[cext.PSUTIL_CONN_NONE]
+ if fam in (AF_INET, AF_INET6):
+ if laddr:
+ laddr = _common.addr(*laddr)
+ if raddr:
+ raddr = _common.addr(*raddr)
fam = sockfam_to_enum(fam)
- laddr = _common.addr(*laddr)
- raddr = _common.addr(*raddr)
type = socktype_to_enum(type)
nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid)
ret.add(nt)
@@ -719,8 +719,10 @@ class Process(object):
except KeyError:
status = TCP_STATUSES[cext.PSUTIL_CONN_NONE]
if fam in (AF_INET, AF_INET6):
- laddr = _common.addr(*laddr)
- raddr = _common.addr(*raddr)
+ if laddr:
+ laddr = _common.addr(*laddr)
+ if raddr:
+ raddr = _common.addr(*raddr)
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
@@ -737,8 +739,10 @@ class Process(object):
for item in rawlist:
fd, fam, type, laddr, raddr, status = item
if fam in (AF_INET, AF_INET6):
- laddr = _common.addr(*laddr)
- raddr = _common.addr(*raddr)
+ if laddr:
+ laddr = _common.addr(*laddr)
+ if raddr:
+ raddr = _common.addr(*raddr)
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
status = TCP_STATUSES[status]
@@ -753,10 +757,7 @@ class Process(object):
@wrap_exceptions
def wait(self, timeout=None):
- try:
- return _psposix.wait_pid(self.pid, timeout)
- except _psposix.TimeoutExpired:
- raise TimeoutExpired(timeout, self.pid, self._name)
+ return _psposix.wait_pid(self.pid, timeout, self._name)
@wrap_exceptions
def nice_get(self):
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index b55f4197..06f1aa6b 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -41,6 +41,9 @@ from ._compat import b
from ._compat import basestring
from ._compat import long
from ._compat import PY3
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
if sys.version_info >= (3, 4):
import enum
@@ -137,12 +140,6 @@ TCP_STATUSES = {
"0B": _common.CONN_CLOSING
}
-# these get overwritten on "import psutil" from the __init__.py file
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
-
# =====================================================================
# --- named tuples
@@ -1142,21 +1139,22 @@ def sensors_temperatures():
basenames = sorted(set([x.split('_')[0] for x in basenames]))
for base in basenames:
+ try:
+ current = float(cat(base + '_input')) / 1000.0
+ except (IOError, OSError) as err:
+ # A lot of things can go wrong here, so let's just skip the
+ # whole entry.
+ # https://github.com/giampaolo/psutil/issues/1009
+ # https://github.com/giampaolo/psutil/issues/1101
+ # https://github.com/giampaolo/psutil/issues/1129
+ warnings.warn("ignoring %r" % err, RuntimeWarning)
+ continue
+
unit_name = cat(os.path.join(os.path.dirname(base), 'name'),
binary=False)
high = cat(base + '_max', fallback=None)
critical = cat(base + '_crit', fallback=None)
label = cat(base + '_label', fallback='', binary=False)
- try:
- current = float(cat(base + '_input')) / 1000.0
- except OSError as err:
- # https://github.com/giampaolo/psutil/issues/1009
- # https://github.com/giampaolo/psutil/issues/1101
- if err.errno in (errno.EIO, errno.ENODEV):
- warnings.warn("ignoring %r" % err, RuntimeWarning)
- continue
- else:
- raise
if high is not None:
high = float(high) / 1000.0
@@ -1169,8 +1167,8 @@ def sensors_temperatures():
def sensors_fans():
- """Return hardware (CPU and others) fans as a dict
- including hardware label, current speed.
+ """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
@@ -1187,11 +1185,14 @@ def sensors_fans():
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)
- current = int(cat(base + '_input'))
-
ret[unit_name].append(_common.sfan(label, current))
return dict(ret)
@@ -1355,6 +1356,30 @@ def pid_exists(pid):
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 EnvironmentError as err:
+ # Note: we should be able to access /stat for all processes
+ # so we won't bump into EPERM, which is good.
+ if err.errno not in (errno.ENOENT, errno.ESRCH,
+ errno.EPERM, errno.EACCES):
+ raise
+ 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.
@@ -1470,9 +1495,17 @@ class Process(object):
if not data:
# may happen in case of zombie process
return []
- if data.endswith('\x00'):
+ # '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]
- return [x for x in data.split('\x00')]
+ return [x for x in data.split(sep)]
@wrap_exceptions
def environ(self):
@@ -1532,10 +1565,7 @@ class Process(object):
@wrap_exceptions
def wait(self, timeout=None):
- try:
- return _psposix.wait_pid(self.pid, timeout)
- except _psposix.TimeoutExpired:
- raise TimeoutExpired(timeout, self.pid, self._name)
+ return _psposix.wait_pid(self.pid, timeout, self._name)
@wrap_exceptions
def create_time(self):
diff --git a/psutil/_psosx.py b/psutil/_psosx.py
index a38efc5b..f149980f 100644
--- a/psutil/_psosx.py
+++ b/psutil/_psosx.py
@@ -23,6 +23,9 @@ from ._common import parse_environ_block
from ._common import sockfam_to_enum
from ._common import socktype_to_enum
from ._common import usage_percent
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
__extra__all__ = []
@@ -85,12 +88,6 @@ pidtaskinfo_map = dict(
volctxsw=7,
)
-# these get overwritten on "import psutil" from the __init__.py file
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
-
# =====================================================================
# --- named tuples
@@ -212,6 +209,29 @@ def disk_partitions(all=False):
# =====================================================================
+# --- 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
# =====================================================================
@@ -282,7 +302,22 @@ def users():
# =====================================================================
-pids = cext.pids
+def pids():
+ ls = cext.pids()
+ if 0 not in ls:
+ # On certain OSX 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.append(0)
+ except NoSuchProcess:
+ pass
+ except AccessDenied:
+ ls.append(0)
+ return ls
+
+
pid_exists = _psposix.pid_exists
@@ -503,10 +538,7 @@ class Process(object):
@wrap_exceptions
def wait(self, timeout=None):
- try:
- return _psposix.wait_pid(self.pid, timeout)
- except _psposix.TimeoutExpired:
- raise TimeoutExpired(timeout, self.pid, self._name)
+ return _psposix.wait_pid(self.pid, timeout, self._name)
@wrap_exceptions
def nice_get(self):
diff --git a/psutil/_psposix.py b/psutil/_psposix.py
index 66d81a3d..6bb8444d 100644
--- a/psutil/_psposix.py
+++ b/psutil/_psposix.py
@@ -15,14 +15,10 @@ from ._common import sdiskusage
from ._common import usage_percent
from ._compat import PY3
from ._compat import unicode
+from ._exceptions import TimeoutExpired
-__all__ = ['TimeoutExpired', 'pid_exists', 'wait_pid', 'disk_usage',
- 'get_terminal_map']
-
-
-class TimeoutExpired(Exception):
- pass
+__all__ = ['pid_exists', 'wait_pid', 'disk_usage', 'get_terminal_map']
def pid_exists(pid):
@@ -53,7 +49,7 @@ def pid_exists(pid):
return True
-def wait_pid(pid, timeout=None):
+def wait_pid(pid, timeout=None, proc_name=None):
"""Wait for process with pid 'pid' to terminate and return its
exit status code as an integer.
@@ -67,7 +63,7 @@ def wait_pid(pid, timeout=None):
def check_timeout(delay):
if timeout is not None:
if timer() >= stop_at:
- raise TimeoutExpired()
+ raise TimeoutExpired(timeout, pid=pid, name=proc_name)
time.sleep(delay)
return min(delay * 2, 0.04)
diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py
index 9931d885..5471d5aa 100644
--- a/psutil/_pssunos.py
+++ b/psutil/_pssunos.py
@@ -24,6 +24,9 @@ from ._common import socktype_to_enum
from ._common import usage_percent
from ._compat import b
from ._compat import PY3
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import ZombieProcess
__extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"]
@@ -78,12 +81,6 @@ proc_info_map = dict(
status=6,
ttynr=7)
-# these get overwritten on "import psutil" from the __init__.py file
-NoSuchProcess = None
-ZombieProcess = None
-AccessDenied = None
-TimeoutExpired = None
-
# =====================================================================
# --- named tuples
@@ -266,8 +263,10 @@ def net_connections(kind, _pid=-1):
if type_ not in types:
continue
if fam in (AF_INET, AF_INET6):
- laddr = _common.addr(*laddr)
- raddr = _common.addr(*raddr)
+ if laddr:
+ laddr = _common.addr(*laddr)
+ if raddr:
+ raddr = _common.addr(*raddr)
status = TCP_STATUSES[status]
fam = sockfam_to_enum(fam)
type_ = socktype_to_enum(type_)
@@ -723,7 +722,4 @@ class Process(object):
@wrap_exceptions
def wait(self, timeout=None):
- try:
- return _psposix.wait_pid(self.pid, timeout)
- except _psposix.TimeoutExpired:
- raise TimeoutExpired(timeout, self.pid, self._name)
+ return _psposix.wait_pid(self.pid, timeout, self._name)
diff --git a/psutil/_psutil_aix.c b/psutil/_psutil_aix.c
new file mode 100644
index 00000000..916254d5
--- /dev/null
+++ b/psutil/_psutil_aix.c
@@ -0,0 +1,988 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'
+ * 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.
+ */
+
+/*
+ * AIX support is experimental at this time.
+ * The following functions and methods are unsupported on the AIX platform:
+ * - psutil.Process.memory_maps
+ *
+ * Known limitations:
+ * - psutil.Process.io_counters read count is always 0
+ * - psutil.Process.threads may not be available on older AIX versions
+ * - reading basic process info may fail or return incorrect values when
+ * process is starting (see IBM APAR IV58499 - fixed in newer AIX versions)
+ * - sockets and pipes may not be counted in num_fds (fixed in newer AIX
+ * versions)
+ *
+ * Useful resources:
+ * - proc filesystem: http://www-01.ibm.com/support/knowledgecenter/
+ * ssw_aix_61/com.ibm.aix.files/proc.htm
+ * - libperfstat: http://www-01.ibm.com/support/knowledgecenter/
+ * ssw_aix_61/com.ibm.aix.files/libperfstat.h.htm
+ */
+
+#include <Python.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/proc.h>
+#include <sys/sysinfo.h>
+#include <sys/procfs.h>
+#include <sys/socket.h>
+#include <sys/thread.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <utmpx.h>
+#include <mntent.h>
+#include <sys/ioctl.h>
+#include <sys/tihdr.h>
+#include <stropts.h>
+#include <netinet/tcp_fsm.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <libperfstat.h>
+
+#include "arch/aix/ifaddrs.h"
+#include "arch/aix/net_connections.h"
+#include "arch/aix/common.h"
+#include "_psutil_common.h"
+#include "_psutil_posix.h"
+
+
+#define TV2DOUBLE(t) (((t).tv_nsec * 0.000000001) + (t).tv_sec)
+
+/*
+ * Read a file content and fills a C structure with it.
+ */
+int
+psutil_file_to_struct(char *path, void *fstruct, size_t size) {
+ int fd;
+ size_t nbytes;
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
+ return 0;
+ }
+ nbytes = read(fd, fstruct, size);
+ if (nbytes <= 0) {
+ close(fd);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return 0;
+ }
+ if (nbytes != size) {
+ close(fd);
+ PyErr_SetString(PyExc_RuntimeError, "structure size mismatch");
+ return 0;
+ }
+ close(fd);
+ return nbytes;
+}
+
+
+/*
+ * Return process ppid, rss, vms, ctime, nice, nthreads, status and tty
+ * as a Python tuple.
+ */
+static PyObject *
+psutil_proc_basic_info(PyObject *self, PyObject *args) {
+ int pid;
+ char path[100];
+ psinfo_t info;
+ pstatus_t status;
+ const char *procfs_path;
+
+ if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+ return NULL;
+
+ sprintf(path, "%s/%i/psinfo", procfs_path, pid);
+ if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+ return NULL;
+
+ if (info.pr_nlwp == 0 && info.pr_lwp.pr_lwpid == 0) {
+ // From the /proc docs: "If the process is a zombie, the pr_nlwp
+ // and pr_lwp.pr_lwpid flags are zero."
+ status.pr_stat = SZOMB;
+ } else if (info.pr_flag & SEXIT) {
+ // "exiting" processes don't have /proc/<pid>/status
+ // There are other "exiting" processes that 'ps' shows as "active"
+ status.pr_stat = SACTIVE;
+ } else {
+ sprintf(path, "%s/%i/status", procfs_path, pid);
+ if (! psutil_file_to_struct(path, (void *)&status, sizeof(status)))
+ return NULL;
+ }
+
+ return Py_BuildValue("KKKdiiiK",
+ (unsigned long long) info.pr_ppid, // parent pid
+ (unsigned long long) info.pr_rssize, // rss
+ (unsigned long long) info.pr_size, // vms
+ TV2DOUBLE(info.pr_start), // create time
+ (int) info.pr_lwp.pr_nice, // nice
+ (int) info.pr_nlwp, // no. of threads
+ (int) status.pr_stat, // status code
+ (unsigned long long)info.pr_ttydev // tty nr
+ );
+}
+
+
+/*
+ * Return process name and args as a Python tuple.
+ */
+static PyObject *
+psutil_proc_name_and_args(PyObject *self, PyObject *args) {
+ int pid;
+ char path[100];
+ psinfo_t info;
+ const char *procfs_path;
+ PyObject *py_name = NULL;
+ PyObject *py_args = NULL;
+ PyObject *py_retlist = NULL;
+
+ if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+ return NULL;
+ sprintf(path, "%s/%i/psinfo", procfs_path, pid);
+ if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+ return NULL;
+
+ py_name = PyUnicode_DecodeFSDefault(info.pr_fname);
+ if (!py_name)
+ goto error;
+ py_args = PyUnicode_DecodeFSDefault(info.pr_psargs);
+ if (!py_args)
+ goto error;
+ py_retlist = Py_BuildValue("OO", py_name, py_args);
+ if (!py_retlist)
+ goto error;
+ Py_DECREF(py_name);
+ Py_DECREF(py_args);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_name);
+ Py_XDECREF(py_args);
+ Py_XDECREF(py_retlist);
+ return NULL;
+}
+
+
+#ifdef CURR_VERSION_THREAD
+/*
+ * Retrieves all threads used by process returning a list of tuples
+ * including thread id, user time and system time.
+ */
+static PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+ long pid;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ perfstat_thread_t *threadt = NULL;
+ perfstat_id_t id;
+ int i, rc, thread_count;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ goto error;
+
+ /* Get the count of threads */
+ thread_count = perfstat_thread(NULL, NULL, sizeof(perfstat_thread_t), 0);
+ if (thread_count <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ /* Allocate enough memory */
+ threadt = (perfstat_thread_t *)calloc(thread_count,
+ sizeof(perfstat_thread_t));
+ if (threadt == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ strcpy(id.name, "");
+ rc = perfstat_thread(&id, threadt, sizeof(perfstat_thread_t),
+ thread_count);
+ if (rc <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < thread_count; i++) {
+ if (threadt[i].pid != pid)
+ continue;
+
+ py_tuple = Py_BuildValue("Idd",
+ threadt[i].tid,
+ threadt[i].ucpu_time,
+ threadt[i].scpu_time);
+ if (py_tuple == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+ free(threadt);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (threadt != NULL)
+ free(threadt);
+ return NULL;
+}
+#endif
+
+
+static PyObject *
+psutil_proc_io_counters(PyObject *self, PyObject *args) {
+ long pid;
+ int rc;
+ perfstat_process_t procinfo;
+ perfstat_id_t id;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+
+ snprintf(id.name, sizeof(id.name), "%ld", pid);
+ rc = perfstat_process(&id, &procinfo, sizeof(perfstat_process_t), 1);
+ if (rc <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ return Py_BuildValue("(KKKK)",
+ procinfo.inOps, // XXX always 0
+ procinfo.outOps,
+ procinfo.inBytes, // XXX always 0
+ procinfo.outBytes);
+}
+
+
+/*
+ * Return process user and system CPU times as a Python tuple.
+ */
+static PyObject *
+psutil_proc_cpu_times(PyObject *self, PyObject *args) {
+ int pid;
+ char path[100];
+ pstatus_t info;
+ const char *procfs_path;
+
+ if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+ return NULL;
+ sprintf(path, "%s/%i/status", procfs_path, pid);
+ if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+ return NULL;
+ // results are more precise than os.times()
+ return Py_BuildValue("dddd",
+ TV2DOUBLE(info.pr_utime),
+ TV2DOUBLE(info.pr_stime),
+ TV2DOUBLE(info.pr_cutime),
+ TV2DOUBLE(info.pr_cstime));
+}
+
+
+/*
+ * Return process uids/gids as a Python tuple.
+ */
+static PyObject *
+psutil_proc_cred(PyObject *self, PyObject *args) {
+ int pid;
+ char path[100];
+ prcred_t info;
+ const char *procfs_path;
+
+ if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
+ return NULL;
+ sprintf(path, "%s/%i/cred", procfs_path, pid);
+ if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
+ return NULL;
+ return Py_BuildValue("iiiiii",
+ info.pr_ruid, info.pr_euid, info.pr_suid,
+ info.pr_rgid, info.pr_egid, info.pr_sgid);
+}
+
+
+/*
+ * Return process voluntary and involuntary context switches as a Python tuple.
+ */
+static PyObject *
+psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {
+ PyObject *py_tuple = NULL;
+ pid32_t requested_pid;
+ pid32_t pid = 0;
+ int np = 0;
+ struct procentry64 *processes = (struct procentry64 *)NULL;
+ struct procentry64 *p;
+
+ if (! PyArg_ParseTuple(args, "i", &requested_pid))
+ return NULL;
+
+ processes = psutil_read_process_table(&np);
+ if (!processes)
+ return NULL;
+
+ /* Loop through processes */
+ for (p = processes; np > 0; np--, p++) {
+ pid = p->pi_pid;
+ if (requested_pid != pid)
+ continue;
+ py_tuple = Py_BuildValue("LL",
+ (long long) p->pi_ru.ru_nvcsw, /* voluntary context switches */
+ (long long) p->pi_ru.ru_nivcsw); /* involuntary */
+ free(processes);
+ return py_tuple;
+ }
+
+ /* finished iteration without finding requested pid */
+ free(processes);
+ return NoSuchProcess("");
+}
+
+
+/*
+ * Return users currently connected on the system.
+ */
+static PyObject *
+psutil_users(PyObject *self, PyObject *args) {
+ struct utmpx *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;
+
+ setutxent();
+ while (NULL != (ut = getutxent())) {
+ 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(
+ "(OOOfOi)",
+ 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 == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_username);
+ Py_DECREF(py_tty);
+ Py_DECREF(py_hostname);
+ Py_DECREF(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);
+ if (ut != NULL)
+ endutxent();
+ return NULL;
+}
+
+
+/*
+ * 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 * mt = 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;
+
+ file = setmntent(MNTTAB, "rb");
+ if (file == NULL) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ mt = getmntent(file);
+ while (mt != NULL) {
+ py_dev = PyUnicode_DecodeFSDefault(mt->mnt_fsname);
+ if (! py_dev)
+ goto error;
+ py_mountp = PyUnicode_DecodeFSDefault(mt->mnt_dir);
+ if (! py_mountp)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(OOss)",
+ py_dev, // device
+ py_mountp, // mount point
+ mt->mnt_type, // fs type
+ mt->mnt_opts); // options
+ if (py_tuple == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_dev);
+ Py_DECREF(py_mountp);
+ Py_DECREF(py_tuple);
+ mt = getmntent(file);
+ }
+ endmntent(file);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_dev);
+ Py_XDECREF(py_mountp);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (file != NULL)
+ endmntent(file);
+ return NULL;
+}
+
+
+/*
+ * Return a list of tuples for network I/O statistics.
+ */
+static PyObject *
+psutil_net_io_counters(PyObject *self, PyObject *args) {
+ perfstat_netinterface_t *statp = NULL;
+ int tot, i;
+ perfstat_id_t first;
+
+ PyObject *py_retdict = PyDict_New();
+ PyObject *py_ifc_info = NULL;
+
+ if (py_retdict == NULL)
+ return NULL;
+
+ /* check how many perfstat_netinterface_t structures are available */
+ tot = perfstat_netinterface(
+ NULL, NULL, sizeof(perfstat_netinterface_t), 0);
+ if (tot == 0) {
+ // no network interfaces - return empty dict
+ return py_retdict;
+ }
+ if (tot < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ statp = (perfstat_netinterface_t *)
+ malloc(tot * sizeof(perfstat_netinterface_t));
+ if (statp == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ strcpy(first.name, FIRST_NETINTERFACE);
+ tot = perfstat_netinterface(&first, statp,
+ sizeof(perfstat_netinterface_t), tot);
+ if (tot < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < tot; i++) {
+ py_ifc_info = Py_BuildValue("(KKKKKKKK)",
+ statp[i].obytes, /* number of bytes sent on interface */
+ statp[i].ibytes, /* number of bytes received on interface */
+ statp[i].opackets, /* number of packets sent on interface */
+ statp[i].ipackets, /* number of packets received on interface */
+ statp[i].ierrors, /* number of input errors on interface */
+ statp[i].oerrors, /* number of output errors on interface */
+ statp[i].if_iqdrops, /* Dropped on input, this interface */
+ statp[i].xmitdrops /* number of packets not transmitted */
+ );
+ if (!py_ifc_info)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, statp[i].name, py_ifc_info))
+ goto error;
+ Py_DECREF(py_ifc_info);
+ }
+
+ free(statp);
+ return py_retdict;
+
+error:
+ if (statp != NULL)
+ free(statp);
+ Py_XDECREF(py_ifc_info);
+ Py_DECREF(py_retdict);
+ return NULL;
+}
+
+
+static PyObject*
+psutil_net_if_stats(PyObject* self, PyObject* args) {
+ char *nic_name;
+ int sock = 0;
+ int ret;
+ int mtu;
+ struct ifreq ifr;
+ PyObject *py_is_up = NULL;
+ PyObject *py_retlist = NULL;
+
+ if (! PyArg_ParseTuple(args, "s", &nic_name))
+ return NULL;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1)
+ goto error;
+
+ strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
+
+ // is up?
+ ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
+ if (ret == -1)
+ goto error;
+ if ((ifr.ifr_flags & IFF_UP) != 0)
+ py_is_up = Py_True;
+ else
+ py_is_up = Py_False;
+ Py_INCREF(py_is_up);
+
+ // MTU
+ ret = ioctl(sock, SIOCGIFMTU, &ifr);
+ if (ret == -1)
+ goto error;
+ mtu = ifr.ifr_mtu;
+
+ close(sock);
+ py_retlist = Py_BuildValue("[Oi]", py_is_up, mtu);
+ if (!py_retlist)
+ goto error;
+ Py_DECREF(py_is_up);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_is_up);
+ if (sock != 0)
+ close(sock);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+}
+
+
+static PyObject *
+psutil_boot_time(PyObject *self, PyObject *args) {
+ float boot_time = 0.0;
+ struct utmpx *ut;
+
+ setutxent();
+ while (NULL != (ut = getutxent())) {
+ if (ut->ut_type == BOOT_TIME) {
+ boot_time = (float)ut->ut_tv.tv_sec;
+ break;
+ }
+ }
+ endutxent();
+ if (boot_time == 0.0) {
+ /* could not find BOOT_TIME in getutxent loop */
+ PyErr_SetString(PyExc_RuntimeError, "can't determine boot time");
+ return NULL;
+ }
+ return Py_BuildValue("f", boot_time);
+}
+
+
+/*
+ * Return a Python list of tuple representing per-cpu times
+ */
+static PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+ int ncpu, rc, i;
+ perfstat_cpu_t *cpu = NULL;
+ perfstat_id_t id;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_cputime = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ /* get the number of cpus in ncpu */
+ ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
+ if (ncpu <= 0){
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ /* allocate enough memory to hold the ncpu structures */
+ cpu = (perfstat_cpu_t *) malloc(ncpu * sizeof(perfstat_cpu_t));
+ if (cpu == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ strcpy(id.name, "");
+ rc = perfstat_cpu(&id, cpu, sizeof(perfstat_cpu_t), ncpu);
+
+ if (rc <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < ncpu; i++) {
+ py_cputime = Py_BuildValue(
+ "(dddd)",
+ (double)cpu[i].user,
+ (double)cpu[i].sys,
+ (double)cpu[i].idle,
+ (double)cpu[i].wait);
+ if (!py_cputime)
+ goto error;
+ if (PyList_Append(py_retlist, py_cputime))
+ goto error;
+ Py_DECREF(py_cputime);
+ }
+ free(cpu);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_cputime);
+ Py_DECREF(py_retlist);
+ if (cpu != NULL)
+ free(cpu);
+ return NULL;
+}
+
+
+/*
+ * Return disk IO statistics.
+ */
+static PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+ PyObject *py_retdict = PyDict_New();
+ PyObject *py_disk_info = NULL;
+ perfstat_disk_t *diskt = NULL;
+ perfstat_id_t id;
+ int i, rc, disk_count;
+
+ if (py_retdict == NULL)
+ return NULL;
+
+ /* Get the count of disks */
+ disk_count = perfstat_disk(NULL, NULL, sizeof(perfstat_disk_t), 0);
+ if (disk_count <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ /* Allocate enough memory */
+ diskt = (perfstat_disk_t *)calloc(disk_count,
+ sizeof(perfstat_disk_t));
+ if (diskt == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ strcpy(id.name, FIRST_DISK);
+ rc = perfstat_disk(&id, diskt, sizeof(perfstat_disk_t),
+ disk_count);
+ if (rc <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < disk_count; i++) {
+ py_disk_info = Py_BuildValue(
+ "KKKKKK",
+ diskt[i].__rxfers,
+ diskt[i].xfers - diskt[i].__rxfers,
+ diskt[i].rblks * diskt[i].bsize,
+ diskt[i].wblks * diskt[i].bsize,
+ diskt[i].rserv / 1000 / 1000, // from nano to milli secs
+ diskt[i].wserv / 1000 / 1000 // from nano to milli secs
+ );
+ if (py_disk_info == NULL)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, diskt[i].name,
+ py_disk_info))
+ goto error;
+ Py_DECREF(py_disk_info);
+ }
+ free(diskt);
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_disk_info);
+ Py_DECREF(py_retdict);
+ if (diskt != NULL)
+ free(diskt);
+ return NULL;
+}
+
+
+/*
+ * Return virtual memory usage statistics.
+ */
+static PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+ int rc;
+ int pagesize = getpagesize();
+ perfstat_memory_total_t memory;
+
+ rc = perfstat_memory_total(
+ NULL, &memory, sizeof(perfstat_memory_total_t), 1);
+ if (rc <= 0){
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ return Py_BuildValue("KKKKK",
+ (unsigned long long) memory.real_total * pagesize,
+ (unsigned long long) memory.real_avail * pagesize,
+ (unsigned long long) memory.real_free * pagesize,
+ (unsigned long long) memory.real_pinned * pagesize,
+ (unsigned long long) memory.real_inuse * pagesize
+ );
+}
+
+
+/*
+ * Return stats about swap memory.
+ */
+static PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+ int rc;
+ int pagesize = getpagesize();
+ perfstat_memory_total_t memory;
+
+ rc = perfstat_memory_total(
+ NULL, &memory, sizeof(perfstat_memory_total_t), 1);
+ if (rc <= 0){
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ return Py_BuildValue("KKKK",
+ (unsigned long long) memory.pgsp_total * pagesize,
+ (unsigned long long) memory.pgsp_free * pagesize,
+ (unsigned long long) memory.pgins * pagesize,
+ (unsigned long long) memory.pgouts * pagesize
+ );
+}
+
+
+/*
+ * Return CPU statistics.
+ */
+static PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+ int ncpu, rc, i;
+ // perfstat_cpu_total_t doesn't have invol/vol cswitch, only pswitch
+ // which is apparently something else. We have to sum over all cpus
+ perfstat_cpu_t *cpu = NULL;
+ perfstat_id_t id;
+ u_longlong_t cswitches = 0;
+ u_longlong_t devintrs = 0;
+ u_longlong_t softintrs = 0;
+ u_longlong_t syscall = 0;
+
+ /* get the number of cpus in ncpu */
+ ncpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
+ if (ncpu <= 0){
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ /* allocate enough memory to hold the ncpu structures */
+ cpu = (perfstat_cpu_t *) malloc(ncpu * sizeof(perfstat_cpu_t));
+ if (cpu == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ strcpy(id.name, "");
+ rc = perfstat_cpu(&id, cpu, sizeof(perfstat_cpu_t), ncpu);
+
+ if (rc <= 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < ncpu; i++) {
+ cswitches += cpu[i].invol_cswitch + cpu[i].vol_cswitch;
+ devintrs += cpu[i].devintrs;
+ softintrs += cpu[i].softintrs;
+ syscall += cpu[i].syscall;
+ }
+
+ free(cpu);
+
+ return Py_BuildValue(
+ "KKKK",
+ cswitches,
+ devintrs,
+ softintrs,
+ syscall
+ );
+
+error:
+ if (cpu != NULL)
+ free(cpu);
+ return NULL;
+}
+
+
+/*
+ * define the psutil C module methods and initialize the module.
+ */
+static PyMethodDef
+PsutilMethods[] =
+{
+ // --- process-related functions
+ {"proc_basic_info", psutil_proc_basic_info, METH_VARARGS,
+ "Return process ppid, rss, vms, ctime, nice, nthreads, status and tty"},
+ {"proc_name_and_args", psutil_proc_name_and_args, METH_VARARGS,
+ "Return process name and args."},
+ {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS,
+ "Return process user and system CPU times."},
+ {"proc_cred", psutil_proc_cred, METH_VARARGS,
+ "Return process uids/gids."},
+#ifdef CURR_VERSION_THREAD
+ {"proc_threads", psutil_proc_threads, METH_VARARGS,
+ "Return process threads"},
+#endif
+ {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS,
+ "Get process I/O counters."},
+ {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
+ "Get process I/O counters."},
+
+ // --- system-related functions
+ {"users", psutil_users, METH_VARARGS,
+ "Return currently connected users."},
+ {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
+ "Return disk partitions."},
+ {"boot_time", psutil_boot_time, METH_VARARGS,
+ "Return system boot time in seconds since the EPOCH."},
+ {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
+ "Return system per-cpu times as a list of tuples"},
+ {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
+ "Return a Python dict of tuples for disk I/O statistics."},
+ {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
+ "Return system virtual memory usage statistics"},
+ {"swap_mem", psutil_swap_mem, METH_VARARGS,
+ "Return stats about swap memory, in bytes"},
+ {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
+ "Return a Python dict of tuples for network I/O statistics."},
+ {"net_connections", psutil_net_connections, METH_VARARGS,
+ "Return system-wide connections"},
+ {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
+ "Return NIC stats (isup, mtu)"},
+ {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
+ "Return CPU statistics"},
+
+ // --- 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)
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+
+static int
+psutil_aix_traverse(PyObject *m, visitproc visit, void *arg) {
+ Py_VISIT(GETSTATE(m)->error);
+ return 0;
+}
+
+static int
+psutil_aix_clear(PyObject *m) {
+ Py_CLEAR(GETSTATE(m)->error);
+ return 0;
+}
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "psutil_aix",
+ NULL,
+ sizeof(struct module_state),
+ PsutilMethods,
+ NULL,
+ psutil_aix_traverse,
+ psutil_aix_clear,
+ NULL
+};
+
+#define INITERROR return NULL
+
+PyMODINIT_FUNC PyInit__psutil_aix(void)
+
+#else
+#define INITERROR return
+
+void init_psutil_aix(void)
+#endif
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *module = PyModule_Create(&moduledef);
+#else
+ PyObject *module = Py_InitModule("_psutil_aix", PsutilMethods);
+#endif
+ PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
+
+ PyModule_AddIntConstant(module, "SIDL", SIDL);
+ PyModule_AddIntConstant(module, "SZOMB", SZOMB);
+ PyModule_AddIntConstant(module, "SACTIVE", SACTIVE);
+ PyModule_AddIntConstant(module, "SSWAP", SSWAP);
+ PyModule_AddIntConstant(module, "SSTOP", SSTOP);
+
+ PyModule_AddIntConstant(module, "TCPS_CLOSED", TCPS_CLOSED);
+ PyModule_AddIntConstant(module, "TCPS_CLOSING", TCPS_CLOSING);
+ PyModule_AddIntConstant(module, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT);
+ PyModule_AddIntConstant(module, "TCPS_LISTEN", TCPS_LISTEN);
+ PyModule_AddIntConstant(module, "TCPS_ESTABLISHED", TCPS_ESTABLISHED);
+ PyModule_AddIntConstant(module, "TCPS_SYN_SENT", TCPS_SYN_SENT);
+ PyModule_AddIntConstant(module, "TCPS_SYN_RCVD", TCPS_SYN_RECEIVED);
+ PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1);
+ PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2);
+ PyModule_AddIntConstant(module, "TCPS_LAST_ACK", TCPS_LAST_ACK);
+ PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT);
+ PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
+
+ psutil_setup();
+
+ if (module == NULL)
+ INITERROR;
+#if PY_MAJOR_VERSION >= 3
+ return module;
+#endif
+}
diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c
index 64e1e7f7..09ae0c30 100644
--- a/psutil/_psutil_bsd.c
+++ b/psutil/_psutil_bsd.c
@@ -475,7 +475,7 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
goto error;
}
@@ -986,8 +986,8 @@ PsutilMethods[] = {
#endif
// --- others
- {"py_psutil_testing", py_psutil_testing, METH_VARARGS,
- "Return True if PSUTIL_TESTING env var is set"},
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
{NULL, NULL, 0, NULL}
};
@@ -1090,6 +1090,8 @@ void init_psutil_bsd(void)
// PSUTIL_CONN_NONE
PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", 128);
+ psutil_setup();
+
if (module == NULL)
INITERROR;
#if PY_MAJOR_VERSION >= 3
diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c
index dace4724..908dbf14 100644
--- a/psutil/_psutil_common.c
+++ b/psutil/_psutil_common.c
@@ -9,14 +9,41 @@
#include <Python.h>
#include <stdio.h>
+
+// Global vars.
+int PSUTIL_DEBUG = 0;
+int PSUTIL_TESTING = 0;
+
+
+/*
+ * Backport of unicode FS APIs from Python 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
+ */
+#if PY_MAJOR_VERSION < 3
+PyObject *
+PyUnicode_DecodeFSDefault(char *s) {
+ return PyString_FromString(s);
+}
+
+
+PyObject *
+PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size) {
+ return PyString_FromStringAndSize(s, size);
+}
+#endif
+
+
/*
* Set OSError(errno=ESRCH, strerror="No such process") Python exception.
+ * If msg != "" the exception message will change in accordance.
*/
PyObject *
-NoSuchProcess(void) {
+NoSuchProcess(char *msg) {
PyObject *exc;
- char *msg = strerror(ESRCH);
- exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg);
+ exc = PyObject_CallFunction(
+ PyExc_OSError, "(is)", ESRCH, strlen(msg) ? msg : strerror(ESRCH));
PyErr_SetObject(PyExc_OSError, exc);
Py_XDECREF(exc);
return NULL;
@@ -25,63 +52,53 @@ NoSuchProcess(void) {
/*
* Set OSError(errno=EACCES, strerror="Permission denied") Python exception.
+ * If msg != "" the exception message will change in accordance.
*/
PyObject *
-AccessDenied(void) {
+AccessDenied(char *msg) {
PyObject *exc;
- char *msg = strerror(EACCES);
- exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg);
+ exc = PyObject_CallFunction(
+ PyExc_OSError, "(is)", EACCES, strlen(msg) ? msg : strerror(EACCES));
PyErr_SetObject(PyExc_OSError, exc);
Py_XDECREF(exc);
return NULL;
}
-static int _psutil_testing = -1;
-
-
/*
- * Return 1 if PSUTIL_TESTING env var is set else 0.
+ * 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.
*/
-int
-psutil_testing(void) {
- if (_psutil_testing == -1) {
- if (getenv("PSUTIL_TESTING") != NULL)
- _psutil_testing = 1;
- else
- _psutil_testing = 0;
- }
- return _psutil_testing;
+PyObject *
+psutil_set_testing(PyObject *self, PyObject *args) {
+ PSUTIL_TESTING = 1;
+ Py_INCREF(Py_None);
+ return Py_None;
}
/*
- * Return True if PSUTIL_TESTING env var is set else False.
+ * Print a debug message on stderr. No-op if PSUTIL_DEBUG env var is not set.
*/
-PyObject *
-py_psutil_testing(PyObject *self, PyObject *args) {
- PyObject *res;
- res = psutil_testing() ? Py_True : Py_False;
- Py_INCREF(res);
- return res;
+void
+psutil_debug(const char* format, ...) {
+ va_list argptr;
+ va_start(argptr, format);
+ fprintf(stderr, "psutil-dubug> ");
+ vfprintf(stderr, format, argptr);
+ fprintf(stderr, "\n");
+ va_end(argptr);
}
/*
- * Backport of unicode FS APIs from Python 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
+ * Called on module import on all platforms.
*/
-#if PY_MAJOR_VERSION < 3
-PyObject *
-PyUnicode_DecodeFSDefault(char *s) {
- return PyString_FromString(s);
-}
-
-
-PyObject *
-PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size) {
- return PyString_FromStringAndSize(s, size);
+void
+psutil_setup(void) {
+ if (getenv("PSUTIL_DEBUG") != NULL)
+ PSUTIL_DEBUG = 1;
+ if (getenv("PSUTIL_TESTING") != NULL)
+ PSUTIL_TESTING = 1;
}
-#endif
diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h
index 13404532..3db3f5ed 100644
--- a/psutil/_psutil_common.h
+++ b/psutil/_psutil_common.h
@@ -6,14 +6,20 @@
#include <Python.h>
+extern int PSUTIL_TESTING;
+extern int PSUTIL_DEBUG;
+
// a signaler for connections without an actual status
static const int PSUTIL_CONN_NONE = 128;
-PyObject* AccessDenied(void);
-PyObject* NoSuchProcess(void);
-int psutil_testing(void);
-PyObject* py_psutil_testing(PyObject *self, PyObject *args);
#if PY_MAJOR_VERSION < 3
PyObject* PyUnicode_DecodeFSDefault(char *s);
PyObject* PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size);
#endif
+
+PyObject* AccessDenied(char *msg);
+PyObject* NoSuchProcess(char *msg);
+
+PyObject* psutil_set_testing(PyObject *self, PyObject *args);
+void psutil_debug(const char* format, ...);
+void psutil_setup(void);
diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c
index a15ebe5c..d1f0d145 100644
--- a/psutil/_psutil_linux.c
+++ b/psutil/_psutil_linux.c
@@ -479,7 +479,7 @@ psutil_users(PyObject *self, PyObject *args) {
"(OOOfOi)",
py_username, // username
py_tty, // tty
- py_username, // hostname
+ py_hostname, // hostname
(float)ut->ut_tv.tv_sec, // tstamp
py_user_proc, // (bool) user process
ut->ut_pid // process id
@@ -607,8 +607,8 @@ PsutilMethods[] = {
#endif
// --- others
- {"py_psutil_testing", py_psutil_testing, METH_VARARGS,
- "Return True if PSUTIL_TESTING env var is set"},
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
{NULL, NULL, 0, NULL}
};
@@ -713,6 +713,8 @@ void init_psutil_linux(void)
PyModule_AddIntConstant(module, "DUPLEX_FULL", DUPLEX_FULL);
PyModule_AddIntConstant(module, "DUPLEX_UNKNOWN", DUPLEX_UNKNOWN);
+ psutil_setup();
+
if (module == NULL)
INITERROR;
#if PY_MAJOR_VERSION >= 3
diff --git a/psutil/_psutil_osx.c b/psutil/_psutil_osx.c
index 6e37bca5..f43bb010 100644
--- a/psutil/_psutil_osx.c
+++ b/psutil/_psutil_osx.c
@@ -38,6 +38,8 @@
#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"
@@ -273,9 +275,9 @@ psutil_proc_exe(PyObject *self, PyObject *args) {
ret = proc_pidpath((pid_t)pid, &buf, sizeof(buf));
if (ret == 0) {
if (pid == 0)
- AccessDenied();
+ AccessDenied("");
else
- psutil_raise_for_pid(pid, "proc_pidpath() syscall failed");
+ psutil_raise_for_pid(pid, "proc_pidpath()");
return NULL;
}
return PyUnicode_DecodeFSDefault(buf);
@@ -345,7 +347,16 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
if (err != KERN_SUCCESS) {
- psutil_raise_for_pid(pid, "task_for_pid() failed");
+ if ((err == 5) && (errno == ENOENT)) {
+ // See: https://github.com/giampaolo/psutil/issues/1181
+ psutil_debug("task_for_pid(MACH_PORT_NULL) failed; err=%i, "
+ "errno=%i, msg='%s'\n", err, errno,
+ mach_error_string(err));
+ AccessDenied("");
+ }
+ else {
+ psutil_raise_for_pid(pid, "task_for_pid(MACH_PORT_NULL)");
+ }
goto error;
}
@@ -356,8 +367,15 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
err = vm_region_recurse_64(task, &address, &size, &depth,
(vm_region_info_64_t)&info, &count);
- if (err == KERN_INVALID_ADDRESS)
+ if (err == KERN_INVALID_ADDRESS) {
+ // TODO temporary
+ psutil_debug("vm_region_recurse_64 returned KERN_INVALID_ADDRESS");
break;
+ }
+ if (err != KERN_SUCCESS) {
+ psutil_debug("vm_region_recurse_64 returned != KERN_SUCCESS");
+ }
+
if (info.is_submap) {
depth++;
}
@@ -385,8 +403,9 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
errno = 0;
proc_regionfilename((pid_t)pid, address, buf, sizeof(buf));
if ((errno != 0) || ((sizeof(buf)) <= 0)) {
- psutil_raise_for_pid(
- pid, "proc_regionfilename() syscall failed");
+ // TODO temporary
+ psutil_debug("proc_regionfilename() failed");
+ psutil_raise_for_pid(pid, "proc_regionfilename()");
goto error;
}
@@ -569,9 +588,9 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) {
err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
if (err != KERN_SUCCESS) {
if (psutil_pid_exists(pid) == 0)
- NoSuchProcess();
+ NoSuchProcess("");
else
- AccessDenied();
+ AccessDenied("");
return NULL;
}
@@ -1016,9 +1035,9 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
err = task_for_pid(mach_task_self(), (pid_t)pid, &task);
if (err != KERN_SUCCESS) {
if (psutil_pid_exists(pid) == 0)
- NoSuchProcess();
+ NoSuchProcess("");
else
- AccessDenied();
+ AccessDenied("");
goto error;
}
@@ -1028,7 +1047,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
if (err != KERN_SUCCESS) {
// errcode 4 is "invalid argument" (access denied)
if (err == 4) {
- AccessDenied();
+ AccessDenied("");
}
else {
// otherwise throw a runtime error with appropriate error code
@@ -1138,7 +1157,6 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE);
for (i = 0; i < iterations; i++) {
- py_tuple = NULL;
fdp_pointer = &fds_pointer[i];
if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) {
@@ -1157,7 +1175,8 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
continue;
}
else {
- psutil_raise_for_pid(pid, "proc_pidinfo() syscall failed");
+ psutil_raise_for_pid(
+ pid, "proc_pidinfo(PROC_PIDFDVNODEPATHINFO)");
goto error;
}
}
@@ -1176,7 +1195,9 @@ psutil_proc_open_files(PyObject *self, PyObject *args) {
if (PyList_Append(py_retlist, py_tuple))
goto error;
Py_DECREF(py_tuple);
+ py_tuple = NULL;
Py_DECREF(py_path);
+ py_path = NULL;
// --- /construct python list
}
}
@@ -1267,7 +1288,8 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
continue;
}
else {
- psutil_raise_for_pid(pid, "proc_pidinfo() syscall failed");
+ psutil_raise_for_pid(
+ pid, "proc_pidinfo(PROC_PIDFDSOCKETINFO)");
goto error;
}
}
@@ -1785,6 +1807,92 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
}
+/*
+ * 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.
@@ -1851,10 +1959,12 @@ PsutilMethods[] = {
"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
- {"py_psutil_testing", py_psutil_testing, METH_VARARGS,
- "Return True if PSUTIL_TESTING env var is set"},
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
{NULL, NULL, 0, NULL}
};
@@ -1935,6 +2045,8 @@ init_psutil_osx(void)
PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT);
PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
+ psutil_setup();
+
if (module == NULL)
INITERROR;
#if PY_MAJOR_VERSION >= 3
diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c
index 80c1b8cb..cc827273 100644
--- a/psutil/_psutil_posix.c
+++ b/psutil/_psutil_posix.c
@@ -18,12 +18,15 @@
#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>
#elif defined(PSUTIL_BSD) || defined(PSUTIL_OSX)
#include <netdb.h>
@@ -35,6 +38,8 @@
#elif defined(PSUTIL_SUNOS)
#include <netdb.h>
#include <sys/sockio.h>
+#elif defined(PSUTIL_AIX)
+ #include <netdb.h>
#endif
#include "_psutil_common.h"
@@ -107,16 +112,21 @@ psutil_pid_exists(long pid) {
* This will always set a Python exception and return NULL.
*/
int
-psutil_raise_for_pid(long pid, char *msg) {
+psutil_raise_for_pid(long pid, char *syscall_name) {
// Set exception to AccessDenied if pid exists else NoSuchProcess.
if (errno != 0) {
+ // Unlikely we get here.
PyErr_SetFromErrno(PyExc_OSError);
return 0;
}
- if (psutil_pid_exists(pid) == 0)
- NoSuchProcess();
- else
- PyErr_SetString(PyExc_RuntimeError, msg);
+ else if (psutil_pid_exists(pid) == 0) {
+ psutil_debug("%s syscall failed and PID %i no longer exists; "
+ "assume NoSuchProcess", syscall_name, pid);
+ NoSuchProcess("");
+ }
+ else {
+ PyErr_Format(PyExc_RuntimeError, "%s syscall failed", syscall_name);
+ }
return 0;
}
@@ -688,7 +698,7 @@ void init_psutil_posix(void)
PyObject *module = Py_InitModule("_psutil_posix", PsutilMethods);
#endif
-#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) || defined(PSUTIL_SUNOS)
+#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) || defined(PSUTIL_SUNOS) || defined(PSUTIL_AIX)
PyModule_AddIntConstant(module, "AF_LINK", AF_LINK);
#endif
diff --git a/psutil/_psutil_sunos.c b/psutil/_psutil_sunos.c
index 12caaec7..c6673642 100644
--- a/psutil/_psutil_sunos.c
+++ b/psutil/_psutil_sunos.c
@@ -9,13 +9,13 @@
* this in Cython which I later on translated in C.
*/
-// fix compilation issue on SunOS 5.10, see:
-// https://github.com/giampaolo/psutil/issues/421
-// https://github.com/giampaolo/psutil/issues/1077
-// http://us-east.manta.joyent.com/jmc/public/opensolaris/ARChive/PSARC/2010/111/materials/s10ceval.txt
-//
-// Because LEGACY_MIB_SIZE defined in the same file there is no way to make autoconfiguration =\
-//
+/* fix compilation issue on SunOS 5.10, see:
+ * https://github.com/giampaolo/psutil/issues/421
+ * https://github.com/giampaolo/psutil/issues/1077
+ * http://us-east.manta.joyent.com/jmc/public/opensolaris/ARChive/PSARC/2010/111/materials/s10ceval.txt
+ *
+ * Because LEGACY_MIB_SIZE defined in the same file there is no way to make autoconfiguration =\
+*/
#define NEW_MIB_COMPLIANT 1
#define _STRUCTURED_PROC 1
@@ -126,9 +126,9 @@ psutil_proc_name_and_args(PyObject *self, PyObject *args) {
char path[1000];
psinfo_t info;
const char *procfs_path;
- PyObject *py_name;
- PyObject *py_args;
- PyObject *py_retlist;
+ PyObject *py_name = NULL;
+ PyObject *py_args = NULL;
+ PyObject *py_retlist = NULL;
if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
return NULL;
@@ -185,7 +185,7 @@ psutil_proc_environ(PyObject *self, PyObject *args) {
goto error;
if (! info.pr_envp) {
- AccessDenied();
+ AccessDenied("");
goto error;
}
@@ -328,7 +328,7 @@ psutil_proc_cpu_num(PyObject *self, PyObject *args) {
return Py_BuildValue("i", proc_num);
error:
- if (fd != NULL)
+ if (fd != -1)
close(fd);
if (ptr != NULL)
free(ptr);
@@ -360,7 +360,7 @@ psutil_proc_cred(PyObject *self, PyObject *args) {
/*
- * Return process uids/gids as a Python tuple.
+ * Return process voluntary and involuntary context switches as a Python tuple.
*/
static PyObject *
psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {
@@ -548,6 +548,7 @@ psutil_users(PyObject *self, PyObject *args) {
if (py_retlist == NULL)
return NULL;
+ setutxent();
while (NULL != (ut = getutxent())) {
if (ut->ut_type == USER_PROCESS)
py_user_proc = Py_True;
@@ -580,7 +581,7 @@ psutil_users(PyObject *self, PyObject *args) {
Py_DECREF(py_hostname);
Py_DECREF(py_tuple);
}
- endutent();
+ endutxent();
return py_retlist;
@@ -590,8 +591,7 @@ error:
Py_XDECREF(py_hostname);
Py_XDECREF(py_tuple);
Py_DECREF(py_retlist);
- if (ut != NULL)
- endutent();
+ endutxent();
return NULL;
}
@@ -832,7 +832,7 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
pr_addr_sz = p->pr_vaddr + p->pr_size;
// perms
- sprintf(perms, "%c%c%c%c%c%c", p->pr_mflags & MA_READ ? 'r' : '-',
+ sprintf(perms, "%c%c%c%c", p->pr_mflags & MA_READ ? 'r' : '-',
p->pr_mflags & MA_WRITE ? 'w' : '-',
p->pr_mflags & MA_EXEC ? 'x' : '-',
p->pr_mflags & MA_SHARED ? 's' : '-');
@@ -1332,20 +1332,20 @@ psutil_boot_time(PyObject *self, PyObject *args) {
float boot_time = 0.0;
struct utmpx *ut;
+ setutxent();
while (NULL != (ut = getutxent())) {
if (ut->ut_type == BOOT_TIME) {
boot_time = (float)ut->ut_tv.tv_sec;
break;
}
}
- endutent();
- if (boot_time != 0.0) {
- return Py_BuildValue("f", boot_time);
- }
- else {
+ endutxent();
+ if (boot_time == 0.0) {
+ /* could not find BOOT_TIME in getutxent loop */
PyErr_SetString(PyExc_RuntimeError, "can't determine boot time");
return NULL;
}
+ return Py_BuildValue("f", boot_time);
}
@@ -1592,8 +1592,8 @@ PsutilMethods[] = {
"Return CPU statistics"},
// --- others
- {"py_psutil_testing", py_psutil_testing, METH_VARARGS,
- "Return True if PSUTIL_TESTING env var is set"},
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
{NULL, NULL, 0, NULL}
};
@@ -1679,6 +1679,8 @@ void init_psutil_sunos(void)
PyModule_AddIntConstant(module, "TCPS_BOUND", TCPS_BOUND);
PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
+ psutil_setup();
+
if (module == NULL)
INITERROR;
#if PY_MAJOR_VERSION >= 3
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c
index 0ed4f761..21738e8c 100644
--- a/psutil/_psutil_windows.c
+++ b/psutil/_psutil_windows.c
@@ -25,6 +25,7 @@
#include <wtsapi32.h>
#include <Winsvc.h>
#include <PowrProf.h>
+#include <signal.h>
// Link with Iphlpapi.lib
#pragma comment(lib, "IPHLPAPI.lib")
@@ -334,13 +335,15 @@ psutil_proc_kill(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "l", &pid))
return NULL;
if (pid == 0)
- return AccessDenied();
+ return AccessDenied("");
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (hProcess == NULL) {
if (GetLastError() == ERROR_INVALID_PARAMETER) {
// see https://github.com/giampaolo/psutil/issues/24
- NoSuchProcess();
+ psutil_debug("OpenProcess -> ERROR_INVALID_PARAMETER turned "
+ "into NoSuchProcess");
+ NoSuchProcess("");
}
else {
PyErr_SetFromWindowsErr(0);
@@ -349,7 +352,7 @@ psutil_proc_kill(PyObject *self, PyObject *args) {
}
// kill the process
- if (! TerminateProcess(hProcess, 0)) {
+ if (! TerminateProcess(hProcess, SIGTERM)) {
err = GetLastError();
// See: https://github.com/giampaolo/psutil/issues/1099
if (err != ERROR_ACCESS_DENIED) {
@@ -378,7 +381,7 @@ psutil_proc_wait(PyObject *self, PyObject *args) {
if (! PyArg_ParseTuple(args, "ll", &pid, &timeout))
return NULL;
if (pid == 0)
- return AccessDenied();
+ return AccessDenied("");
hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
FALSE, pid);
@@ -441,7 +444,7 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) {
if (GetLastError() == ERROR_ACCESS_DENIED) {
// usually means the process has died so we throw a NoSuchProcess
// here
- return NoSuchProcess();
+ return NoSuchProcess("");
}
else {
return PyErr_SetFromWindowsErr(0);
@@ -495,7 +498,7 @@ psutil_proc_create_time(PyObject *self, PyObject *args) {
if (GetLastError() == ERROR_ACCESS_DENIED) {
// usually means the process has died so we throw a
// NoSuchProcess here
- return NoSuchProcess();
+ return NoSuchProcess("");
}
else {
return PyErr_SetFromWindowsErr(0);
@@ -514,7 +517,7 @@ psutil_proc_create_time(PyObject *self, PyObject *args) {
CloseHandle(hProcess);
if (ret != 0) {
if (exitCode != STILL_ACTIVE)
- return NoSuchProcess();
+ return NoSuchProcess("");
}
else {
// Ignore access denied as it means the process is still alive.
@@ -628,7 +631,7 @@ psutil_proc_cmdline(PyObject *self, PyObject *args) {
pid_return = psutil_pid_is_running(pid);
if (pid_return == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
if (pid_return == -1)
return NULL;
@@ -651,7 +654,7 @@ psutil_proc_environ(PyObject *self, PyObject *args) {
pid_return = psutil_pid_is_running(pid);
if (pid_return == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
if (pid_return == -1)
return NULL;
@@ -716,7 +719,7 @@ psutil_proc_name(PyObject *self, PyObject *args) {
}
CloseHandle(hSnapShot);
- NoSuchProcess();
+ NoSuchProcess("");
return NULL;
}
@@ -1037,7 +1040,6 @@ error:
/*
* Return process current working directory as a Python string.
*/
-
static PyObject *
psutil_proc_cwd(PyObject *self, PyObject *args) {
long pid;
@@ -1048,7 +1050,7 @@ psutil_proc_cwd(PyObject *self, PyObject *args) {
pid_return = psutil_pid_is_running(pid);
if (pid_return == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
if (pid_return == -1)
return NULL;
@@ -1063,10 +1065,11 @@ int
psutil_proc_suspend_or_resume(DWORD pid, int suspend) {
// a huge thanks to http://www.codeproject.com/KB/threads/pausep.aspx
HANDLE hThreadSnap = NULL;
+ HANDLE hThread;
THREADENTRY32 te32 = {0};
if (pid == 0) {
- AccessDenied();
+ AccessDenied("");
return FALSE;
}
@@ -1088,20 +1091,17 @@ psutil_proc_suspend_or_resume(DWORD pid, int suspend) {
// Walk the thread snapshot to find all threads of the process.
// If the thread belongs to the process, add its information
// to the display list.
- do
- {
- if (te32.th32OwnerProcessID == pid)
- {
- HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE,
- te32.th32ThreadID);
+ do {
+ if (te32.th32OwnerProcessID == pid) {
+ hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE,
+ te32.th32ThreadID);
if (hThread == NULL) {
PyErr_SetFromWindowsErr(0);
CloseHandle(hThread);
CloseHandle(hThreadSnap);
return FALSE;
}
- if (suspend == 1)
- {
+ if (suspend == 1) {
if (SuspendThread(hThread) == (DWORD) - 1) {
PyErr_SetFromWindowsErr(0);
CloseHandle(hThread);
@@ -1109,8 +1109,7 @@ psutil_proc_suspend_or_resume(DWORD pid, int suspend) {
return FALSE;
}
}
- else
- {
+ else {
if (ResumeThread(hThread) == (DWORD) - 1) {
PyErr_SetFromWindowsErr(0);
CloseHandle(hThread);
@@ -1172,13 +1171,13 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
if (pid == 0) {
// raise AD instead of returning 0 as procexp is able to
// retrieve useful information somehow
- AccessDenied();
+ AccessDenied("");
goto error;
}
pid_return = psutil_pid_is_running(pid);
if (pid_return == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
}
if (pid_return == -1)
@@ -1569,7 +1568,7 @@ psutil_net_connections(PyObject *self, PyObject *args) {
pid_return = psutil_pid_is_running(pid);
if (pid_return == 0) {
_psutil_conn_decref_objs();
- return NoSuchProcess();
+ return NoSuchProcess("");
}
else if (pid_return == -1) {
_psutil_conn_decref_objs();
@@ -2303,8 +2302,8 @@ psutil_net_io_counters(PyObject *self, PyObject *args) {
py_nic_info = Py_BuildValue("(KKKKKKKK)",
pIfRow->OutOctets,
pIfRow->InOctets,
- pIfRow->OutUcastPkts,
- pIfRow->InUcastPkts,
+ (pIfRow->OutUcastPkts + pIfRow->OutNUcastPkts),
+ (pIfRow->InUcastPkts + pIfRow->InNUcastPkts),
pIfRow->InErrors,
pIfRow->OutErrors,
pIfRow->InDiscards,
@@ -2313,8 +2312,8 @@ psutil_net_io_counters(PyObject *self, PyObject *args) {
py_nic_info = Py_BuildValue("(kkkkkkkk)",
pIfRow->dwOutOctets,
pIfRow->dwInOctets,
- pIfRow->dwOutUcastPkts,
- pIfRow->dwInUcastPkts,
+ (pIfRow->dwOutUcastPkts + pIfRow->dwOutNUcastPkts),
+ (pIfRow->dwInUcastPkts + pIfRow->dwInNUcastPkts),
pIfRow->dwInErrors,
pIfRow->dwOutErrors,
pIfRow->dwInDiscards,
@@ -2365,6 +2364,9 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) {
char szDevice[MAX_PATH];
char szDeviceDisplay[MAX_PATH];
int devNum;
+ int i;
+ size_t ioctrlSize;
+ BOOL WINAPI ret;
PyObject *py_retdict = PyDict_New();
PyObject *py_tuple = NULL;
@@ -2379,38 +2381,74 @@ psutil_disk_io_counters(PyObject *self, PyObject *args) {
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;
- if (DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0,
- &diskPerformance, sizeof(diskPerformance),
- &dwSize, NULL))
- {
- sprintf_s(szDeviceDisplay, MAX_PATH, "PhysicalDrive%d", devNum);
- py_tuple = Py_BuildValue(
- "(IILLKK)",
- diskPerformance.ReadCount,
- diskPerformance.WriteCount,
- diskPerformance.BytesRead,
- diskPerformance.BytesWritten,
- (unsigned long long)(diskPerformance.ReadTime.QuadPart * 10) / 1000,
- (unsigned long long)(diskPerformance.WriteTime.QuadPart * 10) / 1000);
- if (!py_tuple)
- goto error;
- if (PyDict_SetItemString(py_retdict, szDeviceDisplay,
- py_tuple))
- {
- goto error;
+
+ // 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;
+ }
}
- Py_XDECREF(py_tuple);
- }
- else {
- // XXX we might get here with ERROR_INSUFFICIENT_BUFFER when
- // compiling with mingw32; not sure what to do.
- // return PyErr_SetFromWindowsErr(0);
- ;;
+ 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_XDECREF(py_tuple);
+
+next:
CloseHandle(hDevice);
}
@@ -2451,6 +2489,7 @@ static char *psutil_get_drive_type(int type) {
#define _ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
#endif
+
/*
* Return disk partitions as a list of tuples such as
* (drive_letter, drive_letter, type, "")
@@ -2460,11 +2499,15 @@ 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;
PyObject *py_all;
@@ -2535,6 +2578,40 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
strcat_s(opts, _countof(opts), "rw");
if (pflags & FILE_VOLUME_IS_COMPRESSED)
strcat_s(opts, _countof(opts), ",compressed");
+
+ // 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(
+ "(ssss)",
+ drive_letter,
+ mp_path,
+ fs_type, // Typically NTFS
+ opts);
+
+ if (!py_tuple || PyList_Append(py_retlist, py_tuple) == -1) {
+ FindVolumeMountPointClose(mp_h);
+ goto error;
+ }
+
+ Py_DECREF(py_tuple);
+
+ // Continue looking for more mount points
+ mp_flag = FindNextVolumeMountPoint(mp_h, mp_buf, MAX_PATH);
+ }
+ FindVolumeMountPointClose(mp_h);
+ }
+
+ }
}
if (strlen(opts) > 0)
@@ -3657,8 +3734,8 @@ PsutilMethods[] = {
"QueryDosDevice binding"},
// --- others
- {"py_psutil_testing", py_psutil_testing, METH_VARARGS,
- "Return True if PSUTIL_TESTING env var is set"},
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
{NULL, NULL, 0, NULL}
};
@@ -3804,6 +3881,7 @@ void init_psutil_windows(void)
// set SeDebug for the current process
psutil_set_se_debug();
+ psutil_setup();
#if PY_MAJOR_VERSION >= 3
return module;
diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py
index 4584fd6e..505846e7 100644
--- a/psutil/_pswindows.py
+++ b/psutil/_pswindows.py
@@ -26,7 +26,8 @@ except ImportError as err:
# but if we get here it means this this was a wheel (or exe).
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"
+ msg += "2000, XP and 2003 server; it may be possible that psutil "
+ msg += "will work if compiled from sources though"
raise RuntimeError(msg)
else:
raise
@@ -45,6 +46,9 @@ from ._compat import lru_cache
from ._compat import PY3
from ._compat import unicode
from ._compat import xrange
+from ._exceptions import AccessDenied
+from ._exceptions import NoSuchProcess
+from ._exceptions import TimeoutExpired
from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS
from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS
from ._psutil_windows import HIGH_PRIORITY_CLASS
@@ -139,11 +143,6 @@ pinfo_map = dict(
mem_private=21,
)
-# these get overwritten on "import psutil" from the __init__.py file
-NoSuchProcess = None
-AccessDenied = None
-TimeoutExpired = None
-
# =====================================================================
# --- named tuples
diff --git a/psutil/arch/aix/common.c b/psutil/arch/aix/common.c
new file mode 100644
index 00000000..6115a15d
--- /dev/null
+++ b/psutil/arch/aix/common.c
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#include <Python.h>
+#include <sys/core.h>
+#include <stdlib.h>
+#include "common.h"
+
+/* psutil_kread() - read from kernel memory */
+int
+psutil_kread(
+ int Kd, /* kernel memory file descriptor */
+ KA_T addr, /* kernel memory address */
+ char *buf, /* buffer to receive data */
+ size_t len) { /* length to read */
+ int br;
+
+ if (lseek64(Kd, (off64_t)addr, L_SET) == (off64_t)-1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return 1;
+ }
+ br = read(Kd, buf, len);
+ if (br == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return 1;
+ }
+ if (br != len) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "size mismatch when reading kernel memory fd");
+ return 1;
+ }
+ return 0;
+}
+
+struct procentry64 *
+psutil_read_process_table(int * num) {
+ size_t msz;
+ pid32_t pid = 0;
+ struct procentry64 *processes = (struct procentry64 *)NULL;
+ struct procentry64 *p;
+ int Np = 0; /* number of processes allocated in 'processes' */
+ int np = 0; /* number of processes read into 'processes' */
+ int i; /* number of processes read in current iteration */
+
+ msz = (size_t)(PROCSIZE * PROCINFO_INCR);
+ processes = (struct procentry64 *)malloc(msz);
+ if (!processes) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ Np = PROCINFO_INCR;
+ p = processes;
+ while ((i = getprocs64(p, PROCSIZE, (struct fdsinfo64 *)NULL, 0, &pid,
+ PROCINFO_INCR))
+ == PROCINFO_INCR) {
+ np += PROCINFO_INCR;
+ if (np >= Np) {
+ msz = (size_t)(PROCSIZE * (Np + PROCINFO_INCR));
+ processes = (struct procentry64 *)realloc((char *)processes, msz);
+ if (!processes) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ Np += PROCINFO_INCR;
+ }
+ p = (struct procentry64 *)((char *)processes + (np * PROCSIZE));
+ }
+
+ /* add the number of processes read in the last iteration */
+ if (i > 0)
+ np += i;
+
+ *num = np;
+ return processes;
+} \ No newline at end of file
diff --git a/psutil/arch/aix/common.h b/psutil/arch/aix/common.h
new file mode 100644
index 00000000..b677d8c2
--- /dev/null
+++ b/psutil/arch/aix/common.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef __PSUTIL_AIX_COMMON_H__
+#define __PSUTIL_AIX_COMMON_H__
+
+#include <sys/core.h>
+
+#define PROCINFO_INCR (256)
+#define PROCSIZE (sizeof(struct procentry64))
+#define FDSINFOSIZE (sizeof(struct fdsinfo64))
+#define KMEM "/dev/kmem"
+
+typedef u_longlong_t KA_T;
+
+/* psutil_kread() - read from kernel memory */
+int psutil_kread(int Kd, /* kernel memory file descriptor */
+ KA_T addr, /* kernel memory address */
+ char *buf, /* buffer to receive data */
+ size_t len); /* length to read */
+
+struct procentry64 *
+psutil_read_process_table(
+ int * num /* out - number of processes read */
+);
+
+#endif /* __PSUTIL_AIX_COMMON_H__ */
diff --git a/psutil/arch/aix/ifaddrs.c b/psutil/arch/aix/ifaddrs.c
new file mode 100644
index 00000000..1a819365
--- /dev/null
+++ b/psutil/arch/aix/ifaddrs.c
@@ -0,0 +1,149 @@
+/*
+ * 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
+!*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "ifaddrs.h"
+
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#define SIZE(p) MAX((p).sa_len,sizeof(p))
+
+
+static struct sockaddr *
+sa_dup(struct sockaddr *sa1)
+{
+ struct sockaddr *sa2;
+ size_t sz = sa1->sa_len;
+ sa2 = (struct sockaddr *) calloc(1, sz);
+ if (sa2 == NULL)
+ return NULL;
+ memcpy(sa2, sa1, sz);
+ return sa2;
+}
+
+
+void freeifaddrs(struct ifaddrs *ifp)
+{
+ if (NULL == ifp) return;
+ free(ifp->ifa_name);
+ free(ifp->ifa_addr);
+ free(ifp->ifa_netmask);
+ free(ifp->ifa_dstaddr);
+ freeifaddrs(ifp->ifa_next);
+ free(ifp);
+}
+
+
+int getifaddrs(struct ifaddrs **ifap)
+{
+ int sd, ifsize;
+ char *ccp, *ecp;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ struct ifaddrs *cifa = NULL; /* current */
+ struct ifaddrs *pifa = NULL; /* previous */
+ const size_t IFREQSZ = sizeof(struct ifreq);
+ int fam;
+
+ *ifap = NULL;
+
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd == -1)
+ goto error;
+
+ /* find how much memory to allocate for the SIOCGIFCONF call */
+ if (ioctl(sd, SIOCGSIZIFCONF, (caddr_t)&ifsize) < 0)
+ goto error;
+
+ ifc.ifc_req = (struct ifreq *) calloc(1, ifsize);
+ if (ifc.ifc_req == NULL)
+ goto error;
+ ifc.ifc_len = ifsize;
+
+ if (ioctl(sd, SIOCGIFCONF, &ifc) < 0)
+ goto error;
+
+ ccp = (char *)ifc.ifc_req;
+ ecp = ccp + ifsize;
+
+ while (ccp < ecp) {
+
+ ifr = (struct ifreq *) ccp;
+ ifsize = sizeof(ifr->ifr_name) + SIZE(ifr->ifr_addr);
+ fam = ifr->ifr_addr.sa_family;
+
+ if (fam == AF_INET || fam == AF_INET6) {
+ cifa = (struct ifaddrs *) calloc(1, sizeof(struct ifaddrs));
+ if (cifa == NULL)
+ goto error;
+ cifa->ifa_next = NULL;
+
+ if (pifa == NULL) *ifap = cifa; /* first one */
+ else pifa->ifa_next = cifa;
+
+ cifa->ifa_name = strdup(ifr->ifr_name);
+ if (cifa->ifa_name == NULL)
+ goto error;
+ cifa->ifa_flags = 0;
+ cifa->ifa_dstaddr = NULL;
+
+ cifa->ifa_addr = sa_dup(&ifr->ifr_addr);
+ if (cifa->ifa_addr == NULL)
+ goto error;
+
+ if (fam == AF_INET) {
+ if (ioctl(sd, SIOCGIFNETMASK, ifr, IFREQSZ) < 0)
+ goto error;
+ cifa->ifa_netmask = sa_dup(&ifr->ifr_addr);
+ if (cifa->ifa_netmask == NULL)
+ goto error;
+ }
+
+ if (0 == ioctl(sd, SIOCGIFFLAGS, ifr)) /* optional */
+ cifa->ifa_flags = ifr->ifr_flags;
+
+ if (fam == AF_INET) {
+ if (ioctl(sd, SIOCGIFDSTADDR, ifr, IFREQSZ) < 0) {
+ if (0 == ioctl(sd, SIOCGIFBRDADDR, ifr, IFREQSZ)) {
+ cifa->ifa_dstaddr = sa_dup(&ifr->ifr_addr);
+ if (cifa->ifa_dstaddr == NULL)
+ goto error;
+ }
+ }
+ else {
+ cifa->ifa_dstaddr = sa_dup(&ifr->ifr_addr);
+ if (cifa->ifa_dstaddr == NULL)
+ goto error;
+ }
+ }
+ pifa = cifa;
+ }
+
+ ccp += ifsize;
+ }
+ free(ifc.ifc_req);
+ close(sd);
+ return 0;
+error:
+ if (ifc.ifc_req != NULL)
+ free(ifc.ifc_req);
+ if (sd != -1)
+ close(sd);
+ freeifaddrs(*ifap);
+ return (-1);
+} \ No newline at end of file
diff --git a/psutil/arch/aix/ifaddrs.h b/psutil/arch/aix/ifaddrs.h
new file mode 100644
index 00000000..3920c1cc
--- /dev/null
+++ b/psutil/arch/aix/ifaddrs.h
@@ -0,0 +1,35 @@
+/*
+ * 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 \ No newline at end of file
diff --git a/psutil/arch/aix/net_connections.c b/psutil/arch/aix/net_connections.c
new file mode 100644
index 00000000..69b43892
--- /dev/null
+++ b/psutil/arch/aix/net_connections.c
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+/* Baded on code from lsof:
+ * http://www.ibm.com/developerworks/aix/library/au-lsof.html
+ * - dialects/aix/dproc.c:gather_proc_info
+ * - lib/prfp.c:process_file
+ * - dialects/aix/dsock.c:process_socket
+ * - dialects/aix/dproc.c:get_kernel_access
+*/
+
+#include <Python.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#define _KERNEL
+#include <sys/file.h>
+#undef _KERNEL
+#include <sys/types.h>
+#include <sys/core.h>
+#include <sys/domain.h>
+#include <sys/un.h>
+#include <netinet/in_pcb.h>
+#include <arpa/inet.h>
+
+#include "../../_psutil_common.h"
+#include "net_kernel_structs.h"
+#include "net_connections.h"
+#include "common.h"
+
+#define NO_SOCKET (PyObject *)(-1)
+
+static int
+read_unp_addr(
+ int Kd,
+ KA_T unp_addr,
+ char *buf,
+ size_t buflen
+) {
+ struct sockaddr_un *ua = (struct sockaddr_un *)NULL;
+ struct sockaddr_un un;
+ struct mbuf64 mb;
+ int uo;
+
+ if (psutil_kread(Kd, unp_addr, (char *)&mb, sizeof(mb))) {
+ return 1;
+ }
+
+ uo = (int)(mb.m_hdr.mh_data - unp_addr);
+ if ((uo + sizeof(struct sockaddr)) <= sizeof(mb))
+ ua = (struct sockaddr_un *)((char *)&mb + uo);
+ else {
+ if (psutil_kread(Kd, (KA_T)mb.m_hdr.mh_data,
+ (char *)&un, sizeof(un))) {
+ return 1;
+ }
+ ua = &un;
+ }
+ if (ua && ua->sun_path[0]) {
+ if (mb.m_len > sizeof(struct sockaddr_un))
+ mb.m_len = sizeof(struct sockaddr_un);
+ *((char *)ua + mb.m_len - 1) = '\0';
+ snprintf(buf, buflen, "%s", ua->sun_path);
+ }
+ return 0;
+}
+
+static PyObject *
+process_file(int Kd, pid32_t pid, int fd, KA_T fp) {
+ struct file64 f;
+ struct socket64 s;
+ struct protosw64 p;
+ struct domain d;
+ struct inpcb64 inp;
+ int fam;
+ struct tcpcb64 t;
+ int state = PSUTIL_CONN_NONE;
+ unsigned char *laddr = (unsigned char *)NULL;
+ unsigned char *raddr = (unsigned char *)NULL;
+ int rport, lport;
+ char laddr_str[INET6_ADDRSTRLEN];
+ char raddr_str[INET6_ADDRSTRLEN];
+ struct unpcb64 unp;
+ char unix_laddr_str[PATH_MAX] = { 0 };
+ char unix_raddr_str[PATH_MAX] = { 0 };
+
+ /* Read file structure */
+ if (psutil_kread(Kd, fp, (char *)&f, sizeof(f))) {
+ return NULL;
+ }
+ if (!f.f_count || f.f_type != DTYPE_SOCKET) {
+ return NO_SOCKET;
+ }
+
+ if (psutil_kread(Kd, (KA_T) f.f_data, (char *) &s, sizeof(s))) {
+ return NULL;
+ }
+
+ if (!s.so_type) {
+ return NO_SOCKET;
+ }
+
+ if (!s.so_proto) {
+ PyErr_SetString(PyExc_RuntimeError, "invalid socket protocol handle");
+ return NULL;
+ }
+ if (psutil_kread(Kd, (KA_T)s.so_proto, (char *)&p, sizeof(p))) {
+ return NULL;
+ }
+
+ if (!p.pr_domain) {
+ PyErr_SetString(PyExc_RuntimeError, "invalid socket protocol domain");
+ return NULL;
+ }
+ if (psutil_kread(Kd, (KA_T)p.pr_domain, (char *)&d, sizeof(d))) {
+ return NULL;
+ }
+
+ fam = d.dom_family;
+ if (fam == AF_INET || fam == AF_INET6) {
+ /* Read protocol control block */
+ if (!s.so_pcb) {
+ PyErr_SetString(PyExc_RuntimeError, "invalid socket PCB");
+ return NULL;
+ }
+ if (psutil_kread(Kd, (KA_T) s.so_pcb, (char *) &inp, sizeof(inp))) {
+ return NULL;
+ }
+
+ if (p.pr_protocol == IPPROTO_TCP) {
+ /* If this is a TCP socket, read its control block */
+ if (inp.inp_ppcb
+ && !psutil_kread(Kd, (KA_T)inp.inp_ppcb,
+ (char *)&t, sizeof(t)))
+ state = t.t_state;
+ }
+
+ if (fam == AF_INET6) {
+ laddr = (unsigned char *)&inp.inp_laddr6;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp.inp_faddr6)) {
+ raddr = (unsigned char *)&inp.inp_faddr6;
+ rport = (int)ntohs(inp.inp_fport);
+ }
+ }
+ if (fam == AF_INET) {
+ laddr = (unsigned char *)&inp.inp_laddr;
+ if (inp.inp_faddr.s_addr != INADDR_ANY || inp.inp_fport != 0) {
+ raddr = (unsigned char *)&inp.inp_faddr;
+ rport = (int)ntohs(inp.inp_fport);
+ }
+ }
+ lport = (int)ntohs(inp.inp_lport);
+
+ inet_ntop(fam, laddr, laddr_str, sizeof(laddr_str));
+
+ if (raddr != NULL) {
+ inet_ntop(fam, raddr, raddr_str, sizeof(raddr_str));
+ return Py_BuildValue("(iii(si)(si)ii)", fd, fam,
+ s.so_type, laddr_str, lport, raddr_str,
+ rport, state, pid);
+ }
+ else {
+ return Py_BuildValue("(iii(si)()ii)", fd, fam,
+ s.so_type, laddr_str, lport, state,
+ pid);
+ }
+ }
+
+
+ if (fam == AF_UNIX) {
+ if (psutil_kread(Kd, (KA_T) s.so_pcb, (char *)&unp, sizeof(unp))) {
+ return NULL;
+ }
+ if ((KA_T) f.f_data != (KA_T) unp.unp_socket) {
+ PyErr_SetString(PyExc_RuntimeError, "unp_socket mismatch");
+ return NULL;
+ }
+
+ if (unp.unp_addr) {
+ if (read_unp_addr(Kd, unp.unp_addr, unix_laddr_str,
+ sizeof(unix_laddr_str))) {
+ return NULL;
+ }
+ }
+
+ if (unp.unp_conn) {
+ if (psutil_kread(Kd, (KA_T) unp.unp_conn, (char *)&unp,
+ sizeof(unp))) {
+ return NULL;
+ }
+ if (read_unp_addr(Kd, unp.unp_addr, unix_raddr_str,
+ sizeof(unix_raddr_str))) {
+ return NULL;
+ }
+ }
+
+ return Py_BuildValue("(iiissii)", fd, d.dom_family,
+ s.so_type, unix_laddr_str, unix_raddr_str, PSUTIL_CONN_NONE,
+ pid);
+ }
+ return NO_SOCKET;
+}
+
+PyObject *
+psutil_net_connections(PyObject *self, PyObject *args) {
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ KA_T fp;
+ int Kd = -1;
+ int i, np;
+ struct procentry64 *p;
+ struct fdsinfo64 *fds = (struct fdsinfo64 *)NULL;
+ pid32_t requested_pid;
+ pid32_t pid;
+ struct procentry64 *processes = (struct procentry64 *)NULL;
+ /* the process table */
+
+ if (py_retlist == NULL)
+ goto error;
+ if (! PyArg_ParseTuple(args, "i", &requested_pid))
+ goto error;
+
+ Kd = open(KMEM, O_RDONLY, 0);
+ if (Kd < 0) {
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, KMEM);
+ goto error;
+ }
+
+ processes = psutil_read_process_table(&np);
+ if (!processes)
+ goto error;
+
+ /* Loop through processes */
+ for (p = processes; np > 0; np--, p++) {
+ pid = p->pi_pid;
+ if (requested_pid != -1 && requested_pid != pid)
+ continue;
+ if (p->pi_state == 0 || p->pi_state == SZOMB)
+ continue;
+
+ if (!fds) {
+ fds = (struct fdsinfo64 *)malloc((size_t)FDSINFOSIZE);
+ if (!fds) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ }
+ if (getprocs64((struct procentry64 *)NULL, PROCSIZE, fds, FDSINFOSIZE,
+ &pid, 1)
+ != 1)
+ continue;
+
+ /* loop over file descriptors */
+ for (i = 0; i < p->pi_maxofile; i++) {
+ fp = (KA_T)fds->pi_ufd[i].fp;
+ if (fp) {
+ py_tuple = process_file(Kd, p->pi_pid, i, fp);
+ if (py_tuple == NULL)
+ goto error;
+ if (py_tuple != NO_SOCKET) {
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+ }
+ }
+ }
+ close(Kd);
+ free(processes);
+ if (fds != NULL)
+ free(fds);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (Kd > 0)
+ close(Kd);
+ if (processes != NULL)
+ free(processes);
+ if (fds != NULL)
+ free(fds);
+ return NULL;
+}
diff --git a/psutil/arch/aix/net_connections.h b/psutil/arch/aix/net_connections.h
new file mode 100644
index 00000000..222bcaf3
--- /dev/null
+++ b/psutil/arch/aix/net_connections.h
@@ -0,0 +1,15 @@
+/*
+ * 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.
+ */
+
+#ifndef __NET_CONNECTIONS_H__
+#define __NET_CONNECTIONS_H__
+
+#include <Python.h>
+
+PyObject* psutil_net_connections(PyObject *self, PyObject *args);
+
+#endif /* __NET_CONNECTIONS_H__ */ \ No newline at end of file
diff --git a/psutil/arch/aix/net_kernel_structs.h b/psutil/arch/aix/net_kernel_structs.h
new file mode 100644
index 00000000..09f320ff
--- /dev/null
+++ b/psutil/arch/aix/net_kernel_structs.h
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+/* The kernel is always 64 bit but Python is usually compiled as a 32 bit
+ * process. We're reading the kernel memory to get the network connections,
+ * so we need the structs we read to be defined with 64 bit "pointers".
+ * Here are the partial definitions of the structs we use, taken from the
+ * header files, with data type sizes converted to their 64 bit counterparts,
+ * and unused data truncated. */
+
+#ifdef __64BIT__
+/* In case we're in a 64 bit process after all */
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/unpcb.h>
+#include <sys/mbuf_base.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#define file64 file
+#define socket64 socket
+#define protosw64 protosw
+#define inpcb64 inpcb
+#define tcpcb64 tcpcb
+#define unpcb64 unpcb
+#define mbuf64 mbuf
+#else
+ struct file64 {
+ int f_flag;
+ int f_count;
+ int f_options;
+ int f_type;
+ u_longlong_t f_data;
+ };
+
+ struct socket64 {
+ short so_type; /* generic type, see socket.h */
+ short so_options; /* from socket call, see socket.h */
+ ushort so_linger; /* time to linger while closing */
+ short so_state; /* internal state flags SS_*, below */
+ u_longlong_t so_pcb; /* protocol control block */
+ u_longlong_t so_proto; /* protocol handle */
+ };
+
+ struct protosw64 {
+ short pr_type; /* socket type used for */
+ u_longlong_t pr_domain; /* domain protocol a member of */
+ short pr_protocol; /* protocol number */
+ short pr_flags; /* see below */
+ };
+
+ struct inpcb64 {
+ u_longlong_t inp_next,inp_prev;
+ /* pointers to other pcb's */
+ u_longlong_t inp_head; /* pointer back to chain of inpcb's
+ for this protocol */
+ u_int32_t inp_iflowinfo; /* input flow label */
+ u_short inp_fport; /* foreign port */
+ u_int16_t inp_fatype; /* foreign address type */
+ union in_addr_6 inp_faddr_6; /* foreign host table entry */
+ u_int32_t inp_oflowinfo; /* output flow label */
+ u_short inp_lport; /* local port */
+ u_int16_t inp_latype; /* local address type */
+ union in_addr_6 inp_laddr_6; /* local host table entry */
+ u_longlong_t inp_socket; /* back pointer to socket */
+ u_longlong_t inp_ppcb; /* pointer to per-protocol pcb */
+ u_longlong_t space_rt;
+ struct sockaddr_in6 spare_dst;
+ u_longlong_t inp_ifa; /* interface address to use */
+ int inp_flags; /* generic IP/datagram flags */
+};
+
+struct tcpcb64 {
+ u_longlong_t seg__next;
+ u_longlong_t seg__prev;
+ short t_state; /* state of this connection */
+};
+
+struct unpcb64 {
+ u_longlong_t unp_socket; /* pointer back to socket */
+ u_longlong_t unp_vnode; /* if associated with file */
+ ino_t unp_vno; /* fake vnode number */
+ u_longlong_t unp_conn; /* control block of connected socket */
+ u_longlong_t unp_refs; /* referencing socket linked list */
+ u_longlong_t unp_nextref; /* link in unp_refs list */
+ u_longlong_t unp_addr; /* bound address of socket */
+};
+
+struct m_hdr64
+{
+ u_longlong_t mh_next; /* next buffer in chain */
+ u_longlong_t mh_nextpkt; /* next chain in queue/record */
+ long mh_len; /* amount of data in this mbuf */
+ u_longlong_t mh_data; /* location of data */
+};
+
+struct mbuf64
+{
+ struct m_hdr64 m_hdr;
+};
+
+#define m_len m_hdr.mh_len
+
+#endif \ No newline at end of file
diff --git a/psutil/arch/freebsd/proc_socks.c b/psutil/arch/freebsd/proc_socks.c
index 9b03e059..a458a01e 100644
--- a/psutil/arch/freebsd/proc_socks.c
+++ b/psutil/arch/freebsd/proc_socks.c
@@ -136,20 +136,36 @@ psutil_search_tcplist(char *buf, struct kinfo_file *kif) {
if (kif->kf_sock_domain == AF_INET) {
if (!psutil_sockaddr_matches(
AF_INET, inp->inp_lport, &inp->inp_laddr,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_local))
+#else
+ &kif->kf_un.kf_sock.kf_sa_local))
+#endif
continue;
if (!psutil_sockaddr_matches(
AF_INET, inp->inp_fport, &inp->inp_faddr,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_peer))
+#else
+ &kif->kf_un.kf_sock.kf_sa_peer))
+#endif
continue;
} else {
if (!psutil_sockaddr_matches(
AF_INET6, inp->inp_lport, &inp->in6p_laddr,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_local))
+#else
+ &kif->kf_un.kf_sock.kf_sa_local))
+#endif
continue;
if (!psutil_sockaddr_matches(
AF_INET6, inp->inp_fport, &inp->in6p_faddr,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_peer))
+#else
+ &kif->kf_un.kf_sock.kf_sa_peer))
+#endif
continue;
}
@@ -196,7 +212,7 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
goto error;
}
@@ -243,19 +259,35 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
inet_ntop(
kif->kf_sock_domain,
psutil_sockaddr_addr(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_local),
+#else
+ &kif->kf_un.kf_sock.kf_sa_local),
+#endif
lip,
sizeof(lip));
inet_ntop(
kif->kf_sock_domain,
psutil_sockaddr_addr(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_peer),
+#else
+ &kif->kf_un.kf_sock.kf_sa_peer),
+#endif
rip,
sizeof(rip));
lport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_local));
+#else
+ &kif->kf_un.kf_sock.kf_sa_local));
+#endif
rport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
+#if __FreeBSD_version < 1200031
&kif->kf_sa_peer));
+#else
+ &kif->kf_un.kf_sock.kf_sa_peer));
+#endif
// construct python tuple/list
py_laddr = Py_BuildValue("(si)", lip, lport);
@@ -287,7 +319,11 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
else if (kif->kf_sock_domain == AF_UNIX) {
struct sockaddr_un *sun;
+#if __FreeBSD_version < 1200031
sun = (struct sockaddr_un *)&kif->kf_sa_local;
+#else
+ sun = (struct sockaddr_un *)&kif->kf_un.kf_sock.kf_sa_local;
+#endif
snprintf(
path, sizeof(path), "%.*s",
(int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
diff --git a/psutil/arch/freebsd/specific.c b/psutil/arch/freebsd/specific.c
index 006c813c..52e2ae4a 100644
--- a/psutil/arch/freebsd/specific.c
+++ b/psutil/arch/freebsd/specific.c
@@ -59,7 +59,7 @@ psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) {
// sysctl stores 0 in the size if we can't find the process information.
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
return -1;
}
return 0;
@@ -297,7 +297,7 @@ psutil_proc_exe(PyObject *self, PyObject *args) {
if (ret == -1)
return NULL;
else if (ret == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
else
strcpy(pathname, "");
}
@@ -354,7 +354,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
goto error;
}
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
}
@@ -370,7 +370,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
goto error;
}
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
}
@@ -548,7 +548,7 @@ psutil_proc_cwd(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
goto error;
}
@@ -597,7 +597,7 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
return NULL;
}
free(freep);
@@ -765,7 +765,7 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getvmmap(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getvmmap() failed");
+ psutil_raise_for_pid(pid, "kinfo_getvmmap()");
goto error;
}
for (i = 0; i < cnt; i++) {
diff --git a/psutil/arch/netbsd/specific.c b/psutil/arch/netbsd/specific.c
index 1dc2080e..cab60d60 100644
--- a/psutil/arch/netbsd/specific.c
+++ b/psutil/arch/netbsd/specific.c
@@ -72,7 +72,7 @@ psutil_kinfo_proc(pid_t pid, kinfo_proc *proc) {
}
// sysctl stores 0 in the size if we can't find the process information.
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
return -1;
}
return 0;
@@ -157,7 +157,7 @@ psutil_proc_exe(PyObject *self, PyObject *args) {
if (ret == -1)
return NULL;
else if (ret == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
else
strcpy(pathname, "");
}
@@ -209,7 +209,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
goto error;
}
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
}
@@ -226,7 +226,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
goto error;
}
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
}
@@ -502,7 +502,7 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
return NULL;
}
free(freep);
diff --git a/psutil/arch/openbsd/specific.c b/psutil/arch/openbsd/specific.c
index de30c4d7..33ebdeec 100644
--- a/psutil/arch/openbsd/specific.c
+++ b/psutil/arch/openbsd/specific.c
@@ -67,7 +67,7 @@ psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) {
}
// sysctl stores 0 in the size if we can't find the process information.
if (size == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
return -1;
}
return 0;
@@ -242,7 +242,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
kd = kvm_openfiles(0, 0, 0, O_RDONLY, errbuf);
if (! kd) {
if (strstr(errbuf, "Permission denied") != NULL)
- AccessDenied();
+ AccessDenied("");
else
PyErr_Format(PyExc_RuntimeError, "kvm_openfiles() syscall failed");
goto error;
@@ -253,7 +253,7 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
sizeof(*kp), &nentries);
if (! kp) {
if (strstr(errbuf, "Permission denied") != NULL)
- AccessDenied();
+ AccessDenied("");
else
PyErr_Format(PyExc_RuntimeError, "kvm_getprocs() syscall failed");
goto error;
@@ -404,7 +404,7 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
return NULL;
}
free(freep);
@@ -509,7 +509,7 @@ psutil_proc_connections(PyObject *self, PyObject *args) {
errno = 0;
freep = kinfo_getfile(pid, &cnt);
if (freep == NULL) {
- psutil_raise_for_pid(pid, "kinfo_getfile() failed");
+ psutil_raise_for_pid(pid, "kinfo_getfile()");
goto error;
}
diff --git a/psutil/arch/osx/process_info.c b/psutil/arch/osx/process_info.c
index 7c715be8..40c79a2c 100644
--- a/psutil/arch/osx/process_info.c
+++ b/psutil/arch/osx/process_info.c
@@ -144,7 +144,7 @@ psutil_get_cmdline(long pid) {
// 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_pid_exists(pid)))
- NoSuchProcess();
+ NoSuchProcess("");
else
PyErr_SetFromErrno(PyExc_OSError);
goto error;
@@ -238,7 +238,7 @@ psutil_get_environ(long pid) {
// 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_pid_exists(pid)))
- NoSuchProcess();
+ NoSuchProcess("");
else
PyErr_SetFromErrno(PyExc_OSError);
goto error;
@@ -338,7 +338,7 @@ psutil_get_kinfo_proc(long pid, struct kinfo_proc *kp) {
// sysctl succeeds but len is zero, happens when process has gone away
if (len == 0) {
- NoSuchProcess();
+ NoSuchProcess("");
return -1;
}
return 0;
@@ -354,7 +354,7 @@ psutil_proc_pidinfo(long pid, int flavor, uint64_t arg, void *pti, int size) {
errno = 0;
int ret = proc_pidinfo((int)pid, flavor, arg, pti, size);
if ((ret <= 0) || ((unsigned long)ret < sizeof(pti))) {
- psutil_raise_for_pid(pid, "proc_pidinfo() syscall failed");
+ psutil_raise_for_pid(pid, "proc_pidinfo()");
return 0;
}
return ret;
diff --git a/psutil/arch/solaris/v10/ifaddrs.h b/psutil/arch/solaris/v10/ifaddrs.h
index d2771193..0953a9b9 100644
--- a/psutil/arch/solaris/v10/ifaddrs.h
+++ b/psutil/arch/solaris/v10/ifaddrs.h
@@ -1,8 +1,8 @@
/* Reference: https://lists.samba.org/archive/samba-technical/2009-February/063079.html */
-#ifndef __IFADDRS_H___
-#define __IFADDRS_H___
+#ifndef __IFADDRS_H__
+#define __IFADDRS_H__
#include <sys/socket.h>
#include <net/if.h>
diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h
index 1bbbf2ac..ea23ddb7 100644
--- a/psutil/arch/windows/ntextapi.h
+++ b/psutil/arch/windows/ntextapi.h
@@ -163,13 +163,15 @@ typedef enum _KWAIT_REASON {
} KWAIT_REASON, *PKWAIT_REASON;
-typedef struct _CLIENT_ID {
+typedef struct _CLIENT_ID2 {
HANDLE UniqueProcess;
HANDLE UniqueThread;
-} CLIENT_ID, *PCLIENT_ID;
+} CLIENT_ID2, *PCLIENT_ID2;
+#define CLIENT_ID CLIENT_ID2
+#define PCLIENT_ID PCLIENT_ID2
-typedef struct _SYSTEM_THREAD_INFORMATION {
+typedef struct _SYSTEM_THREAD_INFORMATION2 {
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
@@ -181,8 +183,10 @@ typedef struct _SYSTEM_THREAD_INFORMATION {
ULONG ContextSwitches;
ULONG ThreadState;
KWAIT_REASON WaitReason;
-} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION;
+} SYSTEM_THREAD_INFORMATION2, *PSYSTEM_THREAD_INFORMATION2;
+#define SYSTEM_THREAD_INFORMATION SYSTEM_THREAD_INFORMATION2
+#define PSYSTEM_THREAD_INFORMATION PSYSTEM_THREAD_INFORMATION2
typedef struct _TEB *PTEB;
diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c
index a9687f9c..ffd3c80e 100644
--- a/psutil/arch/windows/process_info.c
+++ b/psutil/arch/windows/process_info.c
@@ -256,7 +256,7 @@ psutil_check_phandle(HANDLE hProcess, DWORD pid) {
if (ret == 1)
return hProcess;
else if (ret == 0)
- return NoSuchProcess();
+ return NoSuchProcess("");
else if (ret == -1)
return PyErr_SetFromWindowsErr(0);
else // -2
@@ -277,7 +277,7 @@ psutil_handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) {
if (pid == 0) {
// otherwise we'd get NoSuchProcess
- return AccessDenied();
+ return AccessDenied("");
}
hProcess = OpenProcess(dwDesiredAccess, FALSE, pid);
@@ -337,7 +337,7 @@ psutil_get_pids(DWORD *numberOfReturnedPIDs) {
int
psutil_assert_pid_exists(DWORD pid, char *err) {
- if (psutil_testing()) {
+ if (PSUTIL_TESTING) {
if (psutil_pid_in_pids(pid) == 0) {
PyErr_SetString(PyExc_AssertionError, err);
return 0;
@@ -349,7 +349,7 @@ psutil_assert_pid_exists(DWORD pid, char *err) {
int
psutil_assert_pid_not_exists(DWORD pid, char *err) {
- if (psutil_testing()) {
+ if (PSUTIL_TESTING) {
if (psutil_pid_in_pids(pid) == 1) {
PyErr_SetString(PyExc_AssertionError, err);
return 0;
@@ -959,7 +959,7 @@ psutil_get_proc_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess,
}
} while ( (process = PSUTIL_NEXT_PROCESS(process)) );
- NoSuchProcess();
+ NoSuchProcess("");
goto error;
error:
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index 7d8e5def..0890b6f9 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -65,7 +65,7 @@ else:
__all__ = [
# constants
'APPVEYOR', 'DEVNULL', 'GLOBAL_TIMEOUT', 'MEMORY_TOLERANCE', 'NO_RETRIES',
- 'PYPY', 'PYTHON', 'ROOT_DIR', 'SCRIPTS_DIR', 'TESTFILE_PREFIX',
+ 'PYPY', 'PYTHON_EXE', 'ROOT_DIR', 'SCRIPTS_DIR', 'TESTFILE_PREFIX',
'TESTFN', 'TESTFN_UNICODE', 'TOX', 'TRAVIS', 'VALID_PROC_STATUSES',
'VERBOSITY',
"HAS_CPU_AFFINITY", "HAS_CPU_FREQ", "HAS_ENVIRON", "HAS_PROC_IO_COUNTERS",
@@ -159,6 +159,7 @@ HAS_MEMORY_FULL_INFO = 'uss' in psutil.Process().memory_full_info()._fields
HAS_MEMORY_MAPS = hasattr(psutil.Process, "memory_maps")
HAS_PROC_CPU_NUM = hasattr(psutil.Process, "cpu_num")
HAS_RLIMIT = hasattr(psutil.Process, "rlimit")
+HAS_THREADS = hasattr(psutil.Process, "threads")
HAS_SENSORS_BATTERY = hasattr(psutil, "sensors_battery")
HAS_BATTERY = HAS_SENSORS_BATTERY and psutil.sensors_battery()
HAS_SENSORS_FANS = hasattr(psutil, "sensors_fans")
@@ -166,7 +167,33 @@ HAS_SENSORS_TEMPERATURES = hasattr(psutil, "sensors_temperatures")
# --- misc
-PYTHON = os.path.realpath(sys.executable)
+
+def _get_py_exe():
+ def attempt(exe):
+ try:
+ subprocess.check_call(
+ [exe, "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except Exception:
+ return None
+ else:
+ return exe
+
+ if OSX:
+ exe = \
+ attempt(sys.executable) or \
+ attempt(os.path.realpath(sys.executable)) or \
+ attempt(which("python%s.%s" % sys.version_info[:2])) or \
+ attempt(psutil.Process().exe())
+ if not exe:
+ raise ValueError("can't find python exe real abspath")
+ return exe
+ else:
+ exe = os.path.realpath(sys.executable)
+ assert os.path.exists(exe), exe
+ return exe
+
+
+PYTHON_EXE = _get_py_exe()
DEVNULL = open(os.devnull, 'r+')
VALID_PROC_STATUSES = [getattr(psutil, x) for x in dir(psutil)
if x.startswith('STATUS_')]
@@ -182,7 +209,11 @@ _testfiles_created = set()
def _cleanup_files():
DEVNULL.close()
for name in os.listdir(u('.')):
- if name.startswith(u(TESTFILE_PREFIX)):
+ if isinstance(name, unicode):
+ prefix = u(TESTFILE_PREFIX)
+ else:
+ prefix = TESTFILE_PREFIX
+ if name.startswith(prefix):
try:
safe_rmpath(name)
except Exception:
@@ -286,7 +317,7 @@ def get_test_subprocess(cmd=None, **kwds):
pyline = "from time import sleep;" \
"open(r'%s', 'w').close();" \
"sleep(60);" % _TESTFN
- cmd = [PYTHON, "-c", pyline]
+ cmd = [PYTHON_EXE, "-c", pyline]
sproc = subprocess.Popen(cmd, **kwds)
_subprocesses_started.add(sproc)
wait_for_file(_TESTFN, delete=True, empty=True)
@@ -308,15 +339,14 @@ def create_proc_children_pair():
_TESTFN2 = os.path.basename(_TESTFN) + '2' # need to be relative
s = textwrap.dedent("""\
import subprocess, os, sys, time
- PYTHON = os.path.realpath(sys.executable)
s = "import os, time;"
s += "f = open('%s', 'w');"
s += "f.write(str(os.getpid()));"
s += "f.close();"
s += "time.sleep(60);"
- subprocess.Popen([PYTHON, '-c', s])
+ subprocess.Popen(['%s', '-c', s])
time.sleep(60)
- """ % _TESTFN2)
+ """ % (_TESTFN2, PYTHON_EXE))
# On Windows if we create a subprocess with CREATE_NO_WINDOW flag
# set (which is the default) a "conhost.exe" extra process will be
# spawned as a child. We don't want that.
@@ -382,7 +412,7 @@ def pyrun(src, **kwds):
_testfiles_created.add(f.name)
f.write(src)
f.flush()
- subp = get_test_subprocess([PYTHON, f.name], **kwds)
+ subp = get_test_subprocess([PYTHON_EXE, f.name], **kwds)
wait_for_pid(subp.pid)
return subp
@@ -689,7 +719,7 @@ def create_exe(outpath, c_code=None):
if c_code:
if not which("gcc"):
raise ValueError("gcc is not installed")
- if c_code is None:
+ if isinstance(c_code, bool): # c_code is True
c_code = textwrap.dedent(
"""
#include <unistd.h>
@@ -698,6 +728,7 @@ def create_exe(outpath, c_code=None):
return 1;
}
""")
+ assert isinstance(c_code, str), c_code
with tempfile.NamedTemporaryFile(
suffix='.c', delete=False, mode='wt') as f:
f.write(c_code)
@@ -707,7 +738,7 @@ def create_exe(outpath, c_code=None):
safe_rmpath(f.name)
else:
# copy python executable
- shutil.copyfile(sys.executable, outpath)
+ shutil.copyfile(PYTHON_EXE, outpath)
if POSIX:
st = os.stat(outpath)
os.chmod(outpath, st.st_mode | stat.S_IEXEC)
@@ -742,16 +773,21 @@ unittest.TestCase = TestCase
def _setup_tests():
- assert 'PSUTIL_TESTING' in os.environ
- assert psutil._psplatform.cext.py_psutil_testing()
+ if 'PSUTIL_TESTING' not in os.environ:
+ # This won't work on Windows but set_testing() below will do it.
+ os.environ['PSUTIL_TESTING'] = '1'
+ psutil._psplatform.cext.set_testing()
def get_suite():
- testmodules = [os.path.splitext(x)[0] for x in os.listdir(HERE)
- if x.endswith('.py') and x.startswith('test_') and not
- x.startswith('test_memory_leaks')]
+ testmods = [os.path.splitext(x)[0] for x in os.listdir(HERE)
+ if x.endswith('.py') and x.startswith('test_') and not
+ x.startswith('test_memory_leaks')]
+ if "WHEELHOUSE_UPLOADER_USERNAME" in os.environ:
+ testmods = [x for x in testmods if not x.endswith((
+ "osx", "posix", "linux"))]
suite = unittest.TestSuite()
- for tm in testmodules:
+ for tm in testmods:
# ...so that the full test paths are printed on screen
tm = "psutil.tests.%s" % tm
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(tm))
diff --git a/psutil/tests/__main__.py b/psutil/tests/__main__.py
index 475e6b81..2cdf5c42 100755
--- a/psutil/tests/__main__.py
+++ b/psutil/tests/__main__.py
@@ -21,11 +21,11 @@ try:
except ImportError:
from urllib2 import urlopen
+from psutil.tests import PYTHON_EXE
from psutil.tests import run_suite
HERE = os.path.abspath(os.path.dirname(__file__))
-PYTHON = os.path.basename(sys.executable)
GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
TEST_DEPS = []
if sys.version_info[:2] == (2, 6):
@@ -54,7 +54,7 @@ def install_pip():
f.flush()
print("installing pip")
- code = os.system('%s %s --user' % (sys.executable, f.name))
+ code = os.system('%s %s --user' % (PYTHON_EXE, f.name))
return code
@@ -68,12 +68,12 @@ def install_test_deps(deps=None):
opts = "--user" if not is_venv else ""
install_pip()
code = os.system('%s -m pip install %s --upgrade %s' % (
- sys.executable, opts, " ".join(deps)))
+ PYTHON_EXE, opts, " ".join(deps)))
return code
def main():
- usage = "%s -m psutil.tests [opts]" % PYTHON
+ usage = "%s -m psutil.tests [opts]" % PYTHON_EXE
parser = optparse.OptionParser(usage=usage, description="run unit tests")
parser.add_option("-i", "--install-deps",
action="store_true", default=False,
@@ -88,8 +88,8 @@ def main():
try:
__import__(dep.split("==")[0])
except ImportError:
- sys.exit("%r lib is not installed; run:\n"
- "%s -m psutil.tests --install-deps" % (dep, PYTHON))
+ sys.exit("%r lib is not installed; run %s -m psutil.tests "
+ "--install-deps" % (dep, PYTHON_EXE))
run_suite()
diff --git a/psutil/tests/test_aix.py b/psutil/tests/test_aix.py
new file mode 100755
index 00000000..7a8a4c33
--- /dev/null
+++ b/psutil/tests/test_aix.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009, Giampaolo Rodola'
+# 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.
+
+"""AIX specific tests."""
+
+import re
+
+from psutil import AIX
+from psutil.tests import run_test_module_by_name
+from psutil.tests import sh
+from psutil.tests import unittest
+import psutil
+
+
+@unittest.skipIf(not AIX, "AIX only")
+class AIXSpecificTestCase(unittest.TestCase):
+
+ def test_virtual_memory(self):
+ out = sh('/usr/bin/svmon -O unit=KB')
+ re_pattern = "memory\s*"
+ for field in ("size inuse free pin virtual available mmode").split():
+ re_pattern += "(?P<%s>\S+)\s+" % (field,)
+ matchobj = re.search(re_pattern, out)
+
+ self.assertIsNotNone(
+ matchobj, "svmon command returned unexpected output")
+
+ KB = 1024
+ total = int(matchobj.group("size")) * KB
+ available = int(matchobj.group("available")) * KB
+ used = int(matchobj.group("inuse")) * KB
+ free = int(matchobj.group("free")) * KB
+
+ psutil_result = psutil.virtual_memory()
+
+ # MEMORY_TOLERANCE from psutil.tests is not enough. For some reason
+ # we're seeing differences of ~1.2 MB. 2 MB is still a good tolerance
+ # when compared to GBs.
+ MEMORY_TOLERANCE = 2 * KB * KB # 2 MB
+ self.assertEqual(psutil_result.total, total)
+ self.assertAlmostEqual(
+ psutil_result.used, used, delta=MEMORY_TOLERANCE)
+ self.assertAlmostEqual(
+ psutil_result.available, available, delta=MEMORY_TOLERANCE)
+ self.assertAlmostEqual(
+ psutil_result.free, free, delta=MEMORY_TOLERANCE)
+
+ def test_swap_memory(self):
+ out = sh('/usr/sbin/lsps -a')
+ # From the man page, "The size is given in megabytes" so we assume
+ # we'll always have 'MB' in the result
+ # TODO maybe try to use "swap -l" to check "used" too, but its units
+ # are not guaranteed to be "MB" so parsing may not be consistent
+ matchobj = re.search("(?P<space>\S+)\s+"
+ "(?P<vol>\S+)\s+"
+ "(?P<vg>\S+)\s+"
+ "(?P<size>\d+)MB", out)
+
+ self.assertIsNotNone(
+ matchobj, "lsps command returned unexpected output")
+
+ total_mb = int(matchobj.group("size"))
+ MB = 1024 ** 2
+ psutil_result = psutil.swap_memory()
+ # we divide our result by MB instead of multiplying the lsps value by
+ # MB because lsps may round down, so we round down too
+ self.assertEqual(int(psutil_result.total / MB), total_mb)
+
+ def test_cpu_stats(self):
+ out = sh('/usr/bin/mpstat -a')
+
+ re_pattern = "ALL\s*"
+ for field in ("min maj mpcs mpcr dev soft dec ph cs ics bound rq "
+ "push S3pull S3grd S0rd S1rd S2rd S3rd S4rd S5rd "
+ "sysc").split():
+ re_pattern += "(?P<%s>\S+)\s+" % (field,)
+ matchobj = re.search(re_pattern, out)
+
+ self.assertIsNotNone(
+ matchobj, "mpstat command returned unexpected output")
+
+ # numbers are usually in the millions so 1000 is ok for tolerance
+ CPU_STATS_TOLERANCE = 1000
+ psutil_result = psutil.cpu_stats()
+ self.assertAlmostEqual(
+ psutil_result.ctx_switches,
+ int(matchobj.group("cs")),
+ delta=CPU_STATS_TOLERANCE)
+ self.assertAlmostEqual(
+ psutil_result.syscalls,
+ int(matchobj.group("sysc")),
+ delta=CPU_STATS_TOLERANCE)
+ self.assertAlmostEqual(
+ psutil_result.interrupts,
+ int(matchobj.group("dev")),
+ delta=CPU_STATS_TOLERANCE)
+ self.assertAlmostEqual(
+ psutil_result.soft_interrupts,
+ int(matchobj.group("soft")),
+ delta=CPU_STATS_TOLERANCE)
+
+ def test_cpu_count_logical(self):
+ out = sh('/usr/bin/mpstat -a')
+ mpstat_lcpu = int(re.search("lcpu=(\d+)", out).group(1))
+ psutil_lcpu = psutil.cpu_count(logical=True)
+ self.assertEqual(mpstat_lcpu, psutil_lcpu)
+
+ def test_net_if_addrs_names(self):
+ out = sh('/etc/ifconfig -l')
+ ifconfig_names = set(out.split())
+ psutil_names = set(psutil.net_if_addrs().keys())
+ self.assertSetEqual(ifconfig_names, psutil_names)
+
+
+if __name__ == '__main__':
+ run_test_module_by_name(__file__)
diff --git a/psutil/tests/test_connections.py b/psutil/tests/test_connections.py
index 203ddebb..176e2664 100755
--- a/psutil/tests/test_connections.py
+++ b/psutil/tests/test_connections.py
@@ -152,6 +152,7 @@ class TestUnconnectedSockets(Base, unittest.TestCase):
assert not conn.raddr
self.assertEqual(conn.status, psutil.CONN_LISTEN)
+ @unittest.skipIf(not supports_ipv6(), "IPv6 not supported")
def test_tcp_v6(self):
addr = ("::1", get_free_port())
with closing(bind_socket(AF_INET6, SOCK_STREAM, addr=addr)) as sock:
@@ -166,6 +167,7 @@ class TestUnconnectedSockets(Base, unittest.TestCase):
assert not conn.raddr
self.assertEqual(conn.status, psutil.CONN_NONE)
+ @unittest.skipIf(not supports_ipv6(), "IPv6 not supported")
def test_udp_v6(self):
addr = ("::1", get_free_port())
with closing(bind_socket(AF_INET6, SOCK_DGRAM, addr=addr)) as sock:
@@ -418,7 +420,7 @@ class TestConnectedSocketPairs(Base, unittest.TestCase):
# =====================================================================
-class TestSystemWideConnections(unittest.TestCase):
+class TestSystemWideConnections(Base, unittest.TestCase):
"""Tests for net_connections()."""
@skip_on_access_denied()
diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py
index 95bf2146..f9543e57 100755
--- a/psutil/tests/test_contracts.py
+++ b/psutil/tests/test_contracts.py
@@ -14,8 +14,10 @@ import os
import stat
import time
import traceback
+import warnings
from contextlib import closing
+from psutil import AIX
from psutil import BSD
from psutil import FREEBSD
from psutil import LINUX
@@ -65,7 +67,8 @@ class TestAvailability(unittest.TestCase):
self.assertEqual(hasattr(psutil, "win_service_get"), WINDOWS)
def test_PROCFS_PATH(self):
- self.assertEqual(hasattr(psutil, "PROCFS_PATH"), LINUX or SUNOS)
+ self.assertEqual(hasattr(psutil, "PROCFS_PATH"),
+ LINUX or SUNOS or AIX)
def test_win_priority(self):
ae = self.assertEqual
@@ -108,7 +111,10 @@ class TestAvailability(unittest.TestCase):
ae(hasattr(psutil, "RLIMIT_SIGPENDING"), hasit)
def test_cpu_freq(self):
- self.assertEqual(hasattr(psutil, "cpu_freq"), LINUX or OSX or WINDOWS)
+ linux = (LINUX and
+ (os.path.exists("/sys/devices/system/cpu/cpufreq") or
+ os.path.exists("/sys/devices/system/cpu/cpu0/cpufreq")))
+ self.assertEqual(hasattr(psutil, "cpu_freq"), linux or OSX or WINDOWS)
def test_sensors_temperatures(self):
self.assertEqual(hasattr(psutil, "sensors_temperatures"), LINUX)
@@ -118,7 +124,7 @@ class TestAvailability(unittest.TestCase):
def test_battery(self):
self.assertEqual(hasattr(psutil, "sensors_battery"),
- LINUX or WINDOWS or FREEBSD)
+ LINUX or WINDOWS or FREEBSD or OSX)
def test_proc_environ(self):
self.assertEqual(hasattr(psutil.Process, "environ"),
@@ -159,7 +165,23 @@ class TestAvailability(unittest.TestCase):
def test_proc_memory_maps(self):
hasit = hasattr(psutil.Process, "memory_maps")
- self.assertEqual(hasit, False if OPENBSD or NETBSD else True)
+ self.assertEqual(hasit, False if OPENBSD or NETBSD or AIX else True)
+
+
+# ===================================================================
+# --- Test deprecations
+# ===================================================================
+
+
+class TestDeprecations(unittest.TestCase):
+
+ def test_memory_info_ex(self):
+ with warnings.catch_warnings(record=True) as ws:
+ psutil.Process().memory_info_ex()
+ w = ws[0]
+ self.assertIsInstance(w.category(), FutureWarning)
+ self.assertIn("memory_info_ex() is deprecated", str(w.message))
+ self.assertIn("use memory_info() instead", str(w.message))
# ===================================================================
@@ -372,12 +394,14 @@ class TestFetchAllProcesses(unittest.TestCase):
self.assertGreaterEqual(ret, 0)
def ppid(self, ret, proc):
- self.assertIsInstance(ret, int)
+ self.assertIsInstance(ret, (int, long))
self.assertGreaterEqual(ret, 0)
def name(self, ret, proc):
self.assertIsInstance(ret, str)
- assert ret
+ # on AIX, "<exiting>" processes don't have names
+ if not AIX:
+ assert ret
def create_time(self, ret, proc):
self.assertIsInstance(ret, float)
@@ -482,7 +506,7 @@ class TestFetchAllProcesses(unittest.TestCase):
for value in ret:
self.assertIsInstance(value, (int, long))
self.assertGreaterEqual(value, 0)
- if POSIX and ret.vms != 0:
+ if POSIX and not AIX and ret.vms != 0:
# VMS is always supposed to be the highest
for name in ret._fields:
if name != 'vms':
@@ -536,8 +560,8 @@ class TestFetchAllProcesses(unittest.TestCase):
check_connection_ntuple(conn)
def cwd(self, ret, proc):
- self.assertIsInstance(ret, str)
- if ret is not None: # BSD may return None
+ if ret: # 'ret' can be None or empty
+ self.assertIsInstance(ret, str)
assert os.path.isabs(ret), ret
try:
st = os.stat(ret)
@@ -607,7 +631,7 @@ class TestFetchAllProcesses(unittest.TestCase):
def num_ctx_switches(self, ret, proc):
assert is_namedtuple(ret)
for value in ret:
- self.assertIsInstance(value, int)
+ self.assertIsInstance(value, (int, long))
self.assertGreaterEqual(value, 0)
def rlimit(self, ret, proc):
diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py
index 468b3c66..6ba17b25 100755
--- a/psutil/tests/test_linux.py
+++ b/psutil/tests/test_linux.py
@@ -29,6 +29,7 @@ from psutil._compat import PY3
from psutil._compat import u
from psutil.tests import call_until
from psutil.tests import HAS_BATTERY
+from psutil.tests import HAS_CPU_FREQ
from psutil.tests import HAS_RLIMIT
from psutil.tests import MEMORY_TOLERANCE
from psutil.tests import mock
@@ -607,11 +608,13 @@ class TestSystemCPU(unittest.TestCase):
self.assertIsNone(psutil._pslinux.cpu_count_physical())
assert m.called
+ @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
def test_cpu_freq_no_result(self):
with mock.patch("psutil._pslinux.glob.glob", return_value=[]):
self.assertIsNone(psutil.cpu_freq())
@unittest.skipIf(TRAVIS, "fails on Travis")
+ @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
def test_cpu_freq_use_second_file(self):
# https://github.com/giampaolo/psutil/issues/981
def glob_mock(pattern):
@@ -629,6 +632,7 @@ class TestSystemCPU(unittest.TestCase):
assert psutil.cpu_freq()
self.assertEqual(len(flags), 2)
+ @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
def test_cpu_freq_emulate_data(self):
def open_mock(name, *args, **kwargs):
if name.endswith('/scaling_cur_freq'):
@@ -651,6 +655,7 @@ class TestSystemCPU(unittest.TestCase):
self.assertEqual(freq.min, 600.0)
self.assertEqual(freq.max, 700.0)
+ @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
def test_cpu_freq_emulate_multi_cpu(self):
def open_mock(name, *args, **kwargs):
if name.endswith('/scaling_cur_freq'):
@@ -675,6 +680,7 @@ class TestSystemCPU(unittest.TestCase):
self.assertEqual(freq.max, 300.0)
@unittest.skipIf(TRAVIS, "fails on Travis")
+ @unittest.skipIf(not HAS_CPU_FREQ, "not supported")
def test_cpu_freq_no_scaling_cur_freq_file(self):
# See: https://github.com/giampaolo/psutil/issues/1071
def open_mock(name, *args, **kwargs):
@@ -762,21 +768,25 @@ class TestSystemNetwork(unittest.TestCase):
# Not always reliable.
# self.assertEqual(stats.isup, 'RUNNING' in out, msg=out)
self.assertEqual(stats.mtu,
- int(re.findall(r'MTU:(\d+)', out)[0]))
+ int(re.findall(r'(?i)MTU[: ](\d+)', out)[0]))
@retry_before_failing()
def test_net_io_counters(self):
def ifconfig(nic):
ret = {}
out = sh("ifconfig %s" % name)
- ret['packets_recv'] = int(re.findall(r'RX packets:(\d+)', out)[0])
- ret['packets_sent'] = int(re.findall(r'TX packets:(\d+)', out)[0])
- ret['errin'] = int(re.findall(r'errors:(\d+)', out)[0])
- ret['errout'] = int(re.findall(r'errors:(\d+)', out)[1])
- ret['dropin'] = int(re.findall(r'dropped:(\d+)', out)[0])
- ret['dropout'] = int(re.findall(r'dropped:(\d+)', out)[1])
- ret['bytes_recv'] = int(re.findall(r'RX bytes:(\d+)', out)[0])
- ret['bytes_sent'] = int(re.findall(r'TX bytes:(\d+)', out)[0])
+ ret['packets_recv'] = int(
+ re.findall(r'RX packets[: ](\d+)', out)[0])
+ ret['packets_sent'] = int(
+ re.findall(r'TX packets[: ](\d+)', out)[0])
+ ret['errin'] = int(re.findall(r'errors[: ](\d+)', out)[0])
+ ret['errout'] = int(re.findall(r'errors[: ](\d+)', out)[1])
+ ret['dropin'] = int(re.findall(r'dropped[: ](\d+)', out)[0])
+ ret['dropout'] = int(re.findall(r'dropped[: ](\d+)', out)[1])
+ ret['bytes_recv'] = int(
+ re.findall(r'RX (?:packets \d+ +)?bytes[: ](\d+)', out)[0])
+ ret['bytes_sent'] = int(
+ re.findall(r'TX (?:packets \d+ +)?bytes[: ](\d+)', out)[0])
return ret
nio = psutil.net_io_counters(pernic=True, nowrap=False)
@@ -1322,7 +1332,9 @@ class TestSensorsBattery(unittest.TestCase):
# Emulate a case where energy_full file does not exist.
# Expected fallback on /capacity.
def open_mock(name, *args, **kwargs):
- if name.startswith("/sys/class/power_supply/BAT0/energy_full"):
+ energy_full = "/sys/class/power_supply/BAT0/energy_full"
+ charge_full = "/sys/class/power_supply/BAT0/charge_full"
+ if name.startswith(energy_full) or name.startswith(charge_full):
raise IOError(errno.ENOENT, "")
elif name.startswith("/sys/class/power_supply/BAT0/capacity"):
return io.BytesIO(b"88")
@@ -1573,6 +1585,20 @@ class TestProcess(unittest.TestCase):
self.assertEqual(p.cmdline(), ['foo', 'bar', ''])
assert m.called
+ def test_cmdline_spaces_mocked(self):
+ # see: https://github.com/giampaolo/psutil/issues/1179
+ p = psutil.Process()
+ fake_file = io.StringIO(u('foo bar '))
+ with mock.patch('psutil._pslinux.open',
+ return_value=fake_file, create=True) as m:
+ self.assertEqual(p.cmdline(), ['foo', 'bar'])
+ assert m.called
+ fake_file = io.StringIO(u('foo bar '))
+ with mock.patch('psutil._pslinux.open',
+ return_value=fake_file, create=True) as m:
+ self.assertEqual(p.cmdline(), ['foo', 'bar', ''])
+ assert m.called
+
def test_readlink_path_deleted_mocked(self):
with mock.patch('psutil._pslinux.os.readlink',
return_value='/home/foo (deleted)'):
diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py
index 85bab84c..f67c0e4c 100755
--- a/psutil/tests/test_misc.py
+++ b/psutil/tests/test_misc.py
@@ -18,7 +18,6 @@ import os
import pickle
import socket
import stat
-import sys
from psutil import LINUX
from psutil import POSIX
@@ -49,6 +48,7 @@ from psutil.tests import HAS_SENSORS_TEMPERATURES
from psutil.tests import import_module_by_path
from psutil.tests import is_namedtuple
from psutil.tests import mock
+from psutil.tests import PYTHON_EXE
from psutil.tests import reap_children
from psutil.tests import reload_module
from psutil.tests import retry
@@ -365,6 +365,8 @@ class TestMisc(unittest.TestCase):
def test_setup_script(self):
setup_py = os.path.join(ROOT_DIR, 'setup.py')
+ if TRAVIS and not os.path.exists(setup_py):
+ return self.skipTest("can't find setup.py")
module = import_module_by_path(setup_py)
self.assertRaises(SystemExit, module.setup)
self.assertEqual(module.get_version(), psutil.__version__)
@@ -643,16 +645,20 @@ class TestWrapNumbers(unittest.TestCase):
@unittest.skipIf(TOX, "can't test on TOX")
+# See: https://travis-ci.org/giampaolo/psutil/jobs/295224806
+@unittest.skipIf(TRAVIS and not os.path.exists(SCRIPTS_DIR),
+ "can't locate scripts directory")
class TestScripts(unittest.TestCase):
"""Tests for scripts in the "scripts" directory."""
@staticmethod
- def assert_stdout(exe, args=None, **kwds):
- exe = '"%s"' % os.path.join(SCRIPTS_DIR, exe)
- if args:
- exe = exe + ' ' + args
+ def assert_stdout(exe, *args, **kwargs):
+ exe = '%s' % os.path.join(SCRIPTS_DIR, exe)
+ cmd = [PYTHON_EXE, exe]
+ for arg in args:
+ cmd.append(arg)
try:
- out = sh(sys.executable + ' ' + exe, **kwds).strip()
+ out = sh(cmd, **kwargs).strip()
except RuntimeError as err:
if 'AccessDenied' in str(err):
return str(err)
@@ -700,7 +706,7 @@ class TestScripts(unittest.TestCase):
self.assert_stdout('meminfo.py')
def test_procinfo(self):
- self.assert_stdout('procinfo.py', args=str(os.getpid()))
+ self.assert_stdout('procinfo.py', str(os.getpid()))
# can't find users on APPVEYOR or TRAVIS
@unittest.skipIf(APPVEYOR or TRAVIS and not psutil.users(),
@@ -724,7 +730,7 @@ class TestScripts(unittest.TestCase):
@unittest.skipIf(not HAS_MEMORY_MAPS, "not supported")
def test_pmap(self):
- self.assert_stdout('pmap.py', args=str(os.getpid()))
+ self.assert_stdout('pmap.py', str(os.getpid()))
@unittest.skipIf(not HAS_MEMORY_FULL_INFO, "not supported")
def test_procsmem(self):
@@ -743,7 +749,7 @@ class TestScripts(unittest.TestCase):
self.assert_syntax('iotop.py')
def test_pidof(self):
- output = self.assert_stdout('pidof.py', args=psutil.Process().name())
+ output = self.assert_stdout('pidof.py', psutil.Process().name())
self.assertIn(str(os.getpid()), output)
@unittest.skipIf(not WINDOWS, "WINDOWS only")
@@ -1014,7 +1020,8 @@ class TestNetUtils(unittest.TestCase):
# work around http://bugs.python.org/issue30204
types[s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)] += 1
self.assertGreaterEqual(fams[socket.AF_INET], 2)
- self.assertGreaterEqual(fams[socket.AF_INET6], 2)
+ if supports_ipv6():
+ self.assertGreaterEqual(fams[socket.AF_INET6], 2)
if POSIX and HAS_CONNECTIONS_UNIX:
self.assertGreaterEqual(fams[socket.AF_UNIX], 2)
self.assertGreaterEqual(types[socket.SOCK_STREAM], 2)
diff --git a/psutil/tests/test_osx.py b/psutil/tests/test_osx.py
index c8214f14..bcb2ba4e 100755
--- a/psutil/tests/test_osx.py
+++ b/psutil/tests/test_osx.py
@@ -14,6 +14,7 @@ import psutil
from psutil import OSX
from psutil.tests import create_zombie_proc
from psutil.tests import get_test_subprocess
+from psutil.tests import HAS_BATTERY
from psutil.tests import MEMORY_TOLERANCE
from psutil.tests import reap_children
from psutil.tests import retry_before_failing
@@ -285,6 +286,18 @@ class TestSystemAPIs(unittest.TestCase):
self.assertEqual(stats.mtu,
int(re.findall(r'mtu (\d+)', out)[0]))
+ # --- sensors_battery
+
+ @unittest.skipIf(not HAS_BATTERY, "no battery")
+ def test_sensors_battery(self):
+ out = sh("pmset -g batt")
+ percent = re.search("(\d+)%", out).group(1)
+ drawing_from = re.search("Now drawing from '([^']+)'", out).group(1)
+ power_plugged = drawing_from == "AC Power"
+ psutil_result = psutil.sensors_battery()
+ self.assertEqual(psutil_result.power_plugged, power_plugged)
+ self.assertEqual(psutil_result.percent, int(percent))
+
if __name__ == '__main__':
run_test_module_by_name(__file__)
diff --git a/psutil/tests/test_posix.py b/psutil/tests/test_posix.py
index 580abdfd..c59f9a1c 100755
--- a/psutil/tests/test_posix.py
+++ b/psutil/tests/test_posix.py
@@ -10,11 +10,13 @@
import datetime
import errno
import os
+import re
import subprocess
import sys
import time
import psutil
+from psutil import AIX
from psutil import BSD
from psutil import LINUX
from psutil import OPENBSD
@@ -27,7 +29,7 @@ from psutil.tests import APPVEYOR
from psutil.tests import get_kernel_version
from psutil.tests import get_test_subprocess
from psutil.tests import mock
-from psutil.tests import PYTHON
+from psutil.tests import PYTHON_EXE
from psutil.tests import reap_children
from psutil.tests import retry_before_failing
from psutil.tests import run_test_module_by_name
@@ -46,8 +48,9 @@ def ps(cmd):
if not LINUX:
cmd = cmd.replace(" --no-headers ", " ")
if SUNOS:
- cmd = cmd.replace("-o command", "-o comm")
cmd = cmd.replace("-o start", "-o stime")
+ if AIX:
+ cmd = cmd.replace("-o rss", "-o rssize")
output = sh(cmd)
if not LINUX:
output = output.split('\n')[1].strip()
@@ -56,6 +59,31 @@ def ps(cmd):
except ValueError:
return output
+# ps "-o" field names differ wildly between platforms.
+# "comm" means "only executable name" but is not available on BSD platforms.
+# "args" means "command with all its arguments", and is also not available
+# on BSD platforms.
+# "command" is like "args" on most platforms, but like "comm" on AIX,
+# and not available on SUNOS.
+# so for the executable name we can use "comm" on Solaris and split "command"
+# on other platforms.
+# to get the cmdline (with args) we have to use "args" on AIX and
+# Solaris, and can use "command" on all others.
+
+
+def ps_name(pid):
+ field = "command"
+ if SUNOS:
+ field = "comm"
+ return ps("ps --no-headers -o %s -p %s" % (field, pid)).split(' ')[0]
+
+
+def ps_args(pid):
+ field = "command"
+ if AIX or SUNOS:
+ field = "args"
+ return ps("ps --no-headers -o %s -p %s" % (field, pid))
+
@unittest.skipIf(not POSIX, "POSIX only")
class TestProcess(unittest.TestCase):
@@ -63,7 +91,7 @@ class TestProcess(unittest.TestCase):
@classmethod
def setUpClass(cls):
- cls.pid = get_test_subprocess([PYTHON, "-E", "-O"],
+ cls.pid = get_test_subprocess([PYTHON_EXE, "-E", "-O"],
stdin=subprocess.PIPE).pid
wait_for_pid(cls.pid)
@@ -121,12 +149,14 @@ class TestProcess(unittest.TestCase):
self.assertEqual(vsz_ps, vsz_psutil)
def test_name(self):
- # use command + arg since "comm" keyword not supported on all platforms
- name_ps = ps("ps --no-headers -o command -p %s" % (
- self.pid)).split(' ')[0]
+ name_ps = ps_name(self.pid)
# remove path if there is any, from the command
name_ps = os.path.basename(name_ps).lower()
name_psutil = psutil.Process(self.pid).name().lower()
+ # ...because of how we calculate PYTHON_EXE; on OSX this may
+ # be "pythonX.Y".
+ name_ps = re.sub(r"\d.\d", "", name_ps)
+ name_psutil = re.sub(r"\d.\d", "", name_psutil)
self.assertEqual(name_ps, name_psutil)
def test_name_long(self):
@@ -179,8 +209,7 @@ class TestProcess(unittest.TestCase):
self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp])
def test_exe(self):
- ps_pathname = ps("ps --no-headers -o command -p %s" %
- self.pid).split(' ')[0]
+ ps_pathname = ps_name(self.pid)
psutil_pathname = psutil.Process(self.pid).exe()
try:
self.assertEqual(ps_pathname, psutil_pathname)
@@ -195,18 +224,17 @@ class TestProcess(unittest.TestCase):
self.assertEqual(ps_pathname, adjusted_ps_pathname)
def test_cmdline(self):
- ps_cmdline = ps("ps --no-headers -o command -p %s" % self.pid)
+ ps_cmdline = ps_args(self.pid)
psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline())
- if SUNOS:
- # ps on Solaris only shows the first part of the cmdline
- psutil_cmdline = psutil_cmdline.split(" ")[0]
self.assertEqual(ps_cmdline, psutil_cmdline)
# On SUNOS "ps" reads niceness /proc/pid/psinfo which returns an
# incorrect value (20); the real deal is getpriority(2) which
# returns 0; psutil relies on it, see:
# https://github.com/giampaolo/psutil/issues/1082
+ # AIX has the same issue
@unittest.skipIf(SUNOS, "not reliable on SUNOS")
+ @unittest.skipIf(AIX, "not reliable on AIX")
def test_nice(self):
ps_nice = ps("ps --no-headers -o nice -p %s" % self.pid)
psutil_nice = psutil.Process().nice()
@@ -262,7 +290,7 @@ class TestSystemAPIs(unittest.TestCase):
def test_pids(self):
# Note: this test might fail if the OS is starting/killing
# other processes in the meantime
- if SUNOS:
+ if SUNOS or AIX:
cmd = ["ps", "-A", "-o", "pid"]
else:
cmd = ["ps", "ax", "-o", "pid"]
@@ -285,11 +313,7 @@ class TestSystemAPIs(unittest.TestCase):
# on OSX and OPENBSD ps doesn't show pid 0
if OSX or OPENBSD and 0 not in pids_ps:
pids_ps.insert(0, 0)
-
- if pids_ps != pids_psutil:
- difference = [x for x in pids_psutil if x not in pids_ps] + \
- [x for x in pids_ps if x not in pids_psutil]
- self.fail("difference: " + str(difference))
+ self.assertEqual(pids_ps, pids_psutil)
# for some reason ifconfig -a does not report all interfaces
# returned by psutil
@@ -355,6 +379,8 @@ class TestSystemAPIs(unittest.TestCase):
psutil._psposix.wait_pid, os.getpid())
assert m.called
+ # AIX can return '-' in df output instead of numbers, e.g. for /proc
+ @unittest.skipIf(AIX, "unreliable on AIX")
def test_disk_usage(self):
def df(device):
out = sh("df -k %s" % device).strip()
diff --git a/psutil/tests/test_process.py b/psutil/tests/test_process.py
index 8b88f766..3b60d38a 100755
--- a/psutil/tests/test_process.py
+++ b/psutil/tests/test_process.py
@@ -9,6 +9,7 @@
import collections
import errno
import getpass
+import itertools
import os
import signal
import socket
@@ -22,6 +23,7 @@ import types
import psutil
+from psutil import AIX
from psutil import BSD
from psutil import LINUX
from psutil import NETBSD
@@ -49,9 +51,10 @@ from psutil.tests import HAS_MEMORY_MAPS
from psutil.tests import HAS_PROC_CPU_NUM
from psutil.tests import HAS_PROC_IO_COUNTERS
from psutil.tests import HAS_RLIMIT
+from psutil.tests import HAS_THREADS
from psutil.tests import mock
from psutil.tests import PYPY
-from psutil.tests import PYTHON
+from psutil.tests import PYTHON_EXE
from psutil.tests import reap_children
from psutil.tests import retry_before_failing
from psutil.tests import run_test_module_by_name
@@ -62,7 +65,6 @@ from psutil.tests import skip_on_not_implemented
from psutil.tests import TESTFILE_PREFIX
from psutil.tests import TESTFN
from psutil.tests import ThreadTask
-from psutil.tests import TOX
from psutil.tests import TRAVIS
from psutil.tests import unittest
from psutil.tests import wait_for_pid
@@ -151,7 +153,7 @@ class TestProcess(unittest.TestCase):
if POSIX:
self.assertEqual(code, -signal.SIGKILL)
else:
- self.assertEqual(code, 0)
+ self.assertEqual(code, signal.SIGTERM)
self.assertFalse(p.is_running())
sproc = get_test_subprocess()
@@ -161,12 +163,12 @@ class TestProcess(unittest.TestCase):
if POSIX:
self.assertEqual(code, -signal.SIGTERM)
else:
- self.assertEqual(code, 0)
+ self.assertEqual(code, signal.SIGTERM)
self.assertFalse(p.is_running())
# check sys.exit() code
code = "import time, sys; time.sleep(0.01); sys.exit(5);"
- sproc = get_test_subprocess([PYTHON, "-c", code])
+ sproc = get_test_subprocess([PYTHON_EXE, "-c", code])
p = psutil.Process(sproc.pid)
self.assertEqual(p.wait(), 5)
self.assertFalse(p.is_running())
@@ -175,7 +177,7 @@ class TestProcess(unittest.TestCase):
# It is not supposed to raise NSP when the process is gone.
# On UNIX this should return None, on Windows it should keep
# returning the exit code.
- sproc = get_test_subprocess([PYTHON, "-c", code])
+ sproc = get_test_subprocess([PYTHON_EXE, "-c", code])
p = psutil.Process(sproc.pid)
self.assertEqual(p.wait(), 5)
self.assertIn(p.wait(), (5, None))
@@ -207,8 +209,8 @@ class TestProcess(unittest.TestCase):
# to get None.
self.assertEqual(ret2, None)
else:
- self.assertEqual(ret1, 0)
- self.assertEqual(ret1, 0)
+ self.assertEqual(ret1, signal.SIGTERM)
+ self.assertEqual(ret1, signal.SIGTERM)
def test_wait_timeout_0(self):
sproc = get_test_subprocess()
@@ -227,7 +229,7 @@ class TestProcess(unittest.TestCase):
if POSIX:
self.assertEqual(code, -signal.SIGKILL)
else:
- self.assertEqual(code, 0)
+ self.assertEqual(code, signal.SIGTERM)
self.assertFalse(p.is_running())
def test_cpu_percent(self):
@@ -316,10 +318,10 @@ class TestProcess(unittest.TestCase):
# test reads
io1 = p.io_counters()
- with open(PYTHON, 'rb') as f:
+ with open(PYTHON_EXE, 'rb') as f:
f.read()
io2 = p.io_counters()
- if not BSD:
+ if not BSD and not AIX:
self.assertGreater(io2.read_count, io1.read_count)
self.assertEqual(io2.write_count, io1.write_count)
if LINUX:
@@ -528,6 +530,7 @@ class TestProcess(unittest.TestCase):
p = psutil.Process()
self.assertGreater(p.num_handles(), 0)
+ @unittest.skipIf(not HAS_THREADS, 'not supported')
def test_threads(self):
p = psutil.Process()
if OPENBSD:
@@ -552,6 +555,7 @@ class TestProcess(unittest.TestCase):
@retry_before_failing()
@skip_on_access_denied(only_if=OSX)
+ @unittest.skipIf(not HAS_THREADS, 'not supported')
def test_threads_2(self):
sproc = get_test_subprocess()
p = psutil.Process(sproc.pid)
@@ -693,12 +697,12 @@ class TestProcess(unittest.TestCase):
sproc = get_test_subprocess()
exe = psutil.Process(sproc.pid).exe()
try:
- self.assertEqual(exe, PYTHON)
+ self.assertEqual(exe, PYTHON_EXE)
except AssertionError:
- if WINDOWS and len(exe) == len(PYTHON):
+ if WINDOWS and len(exe) == len(PYTHON_EXE):
# on Windows we don't care about case sensitivity
normcase = os.path.normcase
- self.assertEqual(normcase(exe), normcase(PYTHON))
+ self.assertEqual(normcase(exe), normcase(PYTHON_EXE))
else:
# certain platforms such as BSD are more accurate returning:
# "/usr/local/bin/python2.7"
@@ -709,7 +713,7 @@ class TestProcess(unittest.TestCase):
ver = "%s.%s" % (sys.version_info[0], sys.version_info[1])
try:
self.assertEqual(exe.replace(ver, ''),
- PYTHON.replace(ver, ''))
+ PYTHON_EXE.replace(ver, ''))
except AssertionError:
# Tipically OSX. Really not sure what to do here.
pass
@@ -718,7 +722,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(out, 'hey')
def test_cmdline(self):
- cmdline = [PYTHON, "-c", "import time; time.sleep(60)"]
+ cmdline = [PYTHON_EXE, "-c", "import time; time.sleep(60)"]
sproc = get_test_subprocess(cmdline)
try:
self.assertEqual(' '.join(psutil.Process(sproc.pid).cmdline()),
@@ -728,27 +732,39 @@ class TestProcess(unittest.TestCase):
# and Open BSD returns a truncated string.
# Also /proc/pid/cmdline behaves the same so it looks
# like this is a kernel bug.
- if NETBSD or OPENBSD:
+ # XXX - AIX truncates long arguments in /proc/pid/cmdline
+ if NETBSD or OPENBSD or AIX:
self.assertEqual(
- psutil.Process(sproc.pid).cmdline()[0], PYTHON)
+ psutil.Process(sproc.pid).cmdline()[0], PYTHON_EXE)
else:
raise
def test_name(self):
- sproc = get_test_subprocess(PYTHON)
+ sproc = get_test_subprocess(PYTHON_EXE)
name = psutil.Process(sproc.pid).name().lower()
pyexe = os.path.basename(os.path.realpath(sys.executable)).lower()
assert pyexe.startswith(name), (pyexe, name)
# XXX
@unittest.skipIf(SUNOS, "broken on SUNOS")
+ @unittest.skipIf(AIX, "broken on AIX")
def test_prog_w_funky_name(self):
# Test that name(), exe() and cmdline() correctly handle programs
# with funky chars such as spaces and ")", see:
# https://github.com/giampaolo/psutil/issues/628
+
+ def rm():
+ # Try to limit occasional failures on Appveyor:
+ # https://ci.appveyor.com/project/giampaolo/psutil/build/1350/
+ # job/lbo3bkju55le850n
+ try:
+ safe_rmpath(funky_path)
+ except OSError:
+ pass
+
funky_path = TESTFN + 'foo bar )'
create_exe(funky_path)
- self.addCleanup(safe_rmpath, funky_path)
+ self.addCleanup(rm)
cmdline = [funky_path, "-c",
"import time; [time.sleep(0.01) for x in range(3000)];"
"arg1", "arg2", "", "arg3", ""]
@@ -853,7 +869,8 @@ class TestProcess(unittest.TestCase):
self.assertEqual(p.cwd(), os.getcwd())
def test_cwd_2(self):
- cmd = [PYTHON, "-c", "import os, time; os.chdir('..'); time.sleep(60)"]
+ cmd = [PYTHON_EXE, "-c",
+ "import os, time; os.chdir('..'); time.sleep(60)"]
sproc = get_test_subprocess(cmd)
p = psutil.Process(sproc.pid)
call_until(p.cwd, "ret == os.path.dirname(os.getcwd())")
@@ -870,10 +887,9 @@ class TestProcess(unittest.TestCase):
self.assertEqual(len(initial), len(set(initial)))
all_cpus = list(range(len(psutil.cpu_percent(percpu=True))))
- # setting on travis doesn't seem to work (always return all
- # CPUs on get):
- # AssertionError: Lists differ: [0, 1, 2, 3, 4, 5, 6, ... != [0]
- for n in all_cpus:
+ # Work around travis failure:
+ # https://travis-ci.org/giampaolo/psutil/builds/284173194
+ for n in all_cpus if not TRAVIS else initial:
p.cpu_affinity([n])
self.assertEqual(p.cpu_affinity(), [n])
if hasattr(os, "sched_getaffinity"):
@@ -911,6 +927,24 @@ class TestProcess(unittest.TestCase):
self.assertRaises(TypeError, p.cpu_affinity, [0, "1"])
self.assertRaises(ValueError, p.cpu_affinity, [0, -1])
+ @unittest.skipIf(not HAS_CPU_AFFINITY, 'not supported')
+ def test_cpu_affinity_all_combinations(self):
+ p = psutil.Process()
+ initial = p.cpu_affinity()
+ assert initial, initial
+ self.addCleanup(p.cpu_affinity, initial)
+
+ # All possible CPU set combinations.
+ combos = []
+ for l in range(0, len(initial) + 1):
+ for subset in itertools.combinations(initial, l):
+ if subset:
+ combos.append(list(subset))
+
+ for combo in combos:
+ p.cpu_affinity(combo)
+ self.assertEqual(p.cpu_affinity(), combo)
+
# TODO: #595
@unittest.skipIf(BSD, "broken on BSD")
# can't find any process file on Appveyor
@@ -937,7 +971,7 @@ class TestProcess(unittest.TestCase):
# another process
cmdline = "import time; f = open(r'%s', 'r'); time.sleep(60);" % TESTFN
- sproc = get_test_subprocess([PYTHON, "-c", cmdline])
+ sproc = get_test_subprocess([PYTHON_EXE, "-c", cmdline])
p = psutil.Process(sproc.pid)
for x in range(100):
@@ -1268,7 +1302,15 @@ class TestProcess(unittest.TestCase):
# set methods
succeed_or_zombie_p_exc(zproc.parent)
if hasattr(zproc, 'cpu_affinity'):
- succeed_or_zombie_p_exc(zproc.cpu_affinity, [0])
+ try:
+ succeed_or_zombie_p_exc(zproc.cpu_affinity, [0])
+ except ValueError as err:
+ if TRAVIS and LINUX and "not eligible" in str(err):
+ # https://travis-ci.org/giampaolo/psutil/jobs/279890461
+ pass
+ else:
+ raise
+
succeed_or_zombie_p_exc(zproc.nice, 0)
if hasattr(zproc, 'ionice'):
if LINUX:
@@ -1294,10 +1336,14 @@ class TestProcess(unittest.TestCase):
# self.assertEqual(zpid.ppid(), os.getpid())
# ...and all other APIs should be able to deal with it
self.assertTrue(psutil.pid_exists(zpid))
- self.assertIn(zpid, psutil.pids())
- self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
- psutil._pmap = {}
- self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
+ if not TRAVIS and OSX:
+ # For some reason this started failing all of the sudden.
+ # Maybe they upgraded OSX version?
+ # https://travis-ci.org/giampaolo/psutil/jobs/310896404
+ self.assertIn(zpid, psutil.pids())
+ self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
+ psutil._pmap = {}
+ self.assertIn(zpid, [x.pid for x in psutil.process_iter()])
@unittest.skipIf(not POSIX, 'POSIX only')
def test_zombie_process_is_running_w_exc(self):
@@ -1360,28 +1406,23 @@ class TestProcess(unittest.TestCase):
@unittest.skipIf(not HAS_ENVIRON, "not supported")
def test_environ(self):
+ def clean_dict(d):
+ # Most of these are problematic on Travis.
+ d.pop("PSUTIL_TESTING", None)
+ d.pop("PLAT", None)
+ d.pop("HOME", None)
+ if OSX:
+ d.pop("__CF_USER_TEXT_ENCODING", None)
+ d.pop("VERSIONER_PYTHON_PREFER_32_BIT", None)
+ d.pop("VERSIONER_PYTHON_VERSION", None)
+ return dict(
+ [(k.rstrip("\r\n"), v.rstrip("\r\n")) for k, v in d.items()])
+
self.maxDiff = None
p = psutil.Process()
- d = p.environ()
- d2 = os.environ.copy()
-
- removes = []
- if 'PSUTIL_TESTING' in os.environ:
- removes.append('PSUTIL_TESTING')
- if OSX:
- removes.extend([
- "__CF_USER_TEXT_ENCODING",
- "VERSIONER_PYTHON_PREFER_32_BIT",
- "VERSIONER_PYTHON_VERSION"])
- if LINUX or OSX:
- removes.extend(['PLAT'])
- if TOX:
- removes.extend(['HOME'])
- for key in removes:
- d.pop(key, None)
- d2.pop(key, None)
-
- self.assertEqual(d, d2)
+ d1 = clean_dict(p.environ())
+ d2 = clean_dict(os.environ.copy())
+ self.assertEqual(d1, d2)
@unittest.skipIf(not HAS_ENVIRON, "not supported")
@unittest.skipIf(not POSIX, "POSIX only")
@@ -1485,7 +1526,7 @@ class TestPopen(unittest.TestCase):
# XXX this test causes a ResourceWarning on Python 3 because
# psutil.__subproc instance doesn't get propertly freed.
# Not sure what to do though.
- cmd = [PYTHON, "-c", "import time; time.sleep(60);"]
+ cmd = [PYTHON_EXE, "-c", "import time; time.sleep(60);"]
with psutil.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) as proc:
proc.name()
@@ -1496,7 +1537,7 @@ class TestPopen(unittest.TestCase):
proc.terminate()
def test_ctx_manager(self):
- with psutil.Popen([PYTHON, "-V"],
+ with psutil.Popen([PYTHON_EXE, "-V"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE) as proc:
@@ -1510,7 +1551,7 @@ class TestPopen(unittest.TestCase):
# subprocess.Popen()'s terminate(), kill() and send_signal() do
# not raise exception after the process is gone. psutil.Popen
# diverges from that.
- cmd = [PYTHON, "-c", "import time; time.sleep(60);"]
+ cmd = [PYTHON_EXE, "-c", "import time; time.sleep(60);"]
with psutil.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) as proc:
proc.terminate()
diff --git a/psutil/tests/test_system.py b/psutil/tests/test_system.py
index e93bb6b5..20b132a9 100755
--- a/psutil/tests/test_system.py
+++ b/psutil/tests/test_system.py
@@ -19,6 +19,7 @@ import tempfile
import time
import psutil
+from psutil import AIX
from psutil import BSD
from psutil import FREEBSD
from psutil import LINUX
@@ -669,7 +670,8 @@ class TestSystemAPIs(unittest.TestCase):
@unittest.skipIf(LINUX and not os.path.exists('/proc/diskstats'),
'/proc/diskstats not available on this linux version')
- @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR") # no visible disks
+ @unittest.skipIf(APPVEYOR and psutil.disk_io_counters() is None,
+ "unreliable on APPVEYOR") # no visible disks
def test_disk_io_counters(self):
def check_ntuple(nt):
self.assertEqual(nt[0], nt.read_count)
@@ -689,6 +691,7 @@ class TestSystemAPIs(unittest.TestCase):
assert getattr(nt, name) >= 0, nt
ret = psutil.disk_io_counters(perdisk=False)
+ assert ret is not None, "no disks on this system?"
check_ntuple(ret)
ret = psutil.disk_io_counters(perdisk=True)
# make sure there are no duplicates
@@ -742,7 +745,8 @@ class TestSystemAPIs(unittest.TestCase):
for name in infos._fields:
value = getattr(infos, name)
self.assertGreaterEqual(value, 0)
- if name in ('ctx_switches', 'interrupts'):
+ # on AIX, ctx_switches is always 0
+ if not AIX and name in ('ctx_switches', 'interrupts'):
self.assertGreater(value, 0)
@unittest.skipIf(not HAS_CPU_FREQ, "not suported")
diff --git a/psutil/tests/test_unicode.py b/psutil/tests/test_unicode.py
index 9b99fdf9..c2a2f847 100755
--- a/psutil/tests/test_unicode.py
+++ b/psutil/tests/test_unicode.py
@@ -49,7 +49,7 @@ etc.) and make sure that:
For a detailed explanation of how psutil handles unicode see:
- https://github.com/giampaolo/psutil/issues/1040
-- https://pythonhosted.org/psutil/#unicode
+- http://psutil.readthedocs.io/#unicode
"""
import os
@@ -91,8 +91,6 @@ import psutil.tests
def safe_rmpath(path):
- # XXX
- return _safe_rmpath(path)
if APPVEYOR:
# TODO - this is quite random and I'm not sure why it happens,
# nor I can reproduce it locally:
@@ -125,8 +123,9 @@ def subprocess_supports_unicode(name):
except UnicodeEncodeError:
return False
else:
- reap_children()
return True
+ finally:
+ reap_children()
# An invalid unicode string.
@@ -145,18 +144,23 @@ else:
class _BaseFSAPIsTests(object):
funky_name = None
- def setUp(self):
- safe_rmpath(self.funky_name)
+ @classmethod
+ def setUpClass(cls):
+ safe_rmpath(cls.funky_name)
+ create_exe(cls.funky_name)
+
+ @classmethod
+ def tearDownClass(cls):
+ reap_children()
+ safe_rmpath(cls.funky_name)
def tearDown(self):
reap_children()
- safe_rmpath(self.funky_name)
def expect_exact_path_match(self):
raise NotImplementedError("must be implemented in subclass")
def test_proc_exe(self):
- create_exe(self.funky_name)
subp = get_test_subprocess(cmd=[self.funky_name])
p = psutil.Process(subp.pid)
exe = p.exe()
@@ -165,7 +169,6 @@ class _BaseFSAPIsTests(object):
self.assertEqual(exe, self.funky_name)
def test_proc_name(self):
- create_exe(self.funky_name)
subp = get_test_subprocess(cmd=[self.funky_name])
if WINDOWS:
# On Windows name() is determined from exe() first, because
@@ -182,7 +185,6 @@ class _BaseFSAPIsTests(object):
self.assertEqual(name, os.path.basename(self.funky_name))
def test_proc_cmdline(self):
- create_exe(self.funky_name)
subp = get_test_subprocess(cmd=[self.funky_name])
p = psutil.Process(subp.pid)
cmdline = p.cmdline()
@@ -192,18 +194,20 @@ class _BaseFSAPIsTests(object):
self.assertEqual(cmdline, [self.funky_name])
def test_proc_cwd(self):
- safe_mkdir(self.funky_name)
- with chdir(self.funky_name):
+ dname = self.funky_name + "2"
+ self.addCleanup(safe_rmpath, dname)
+ safe_mkdir(dname)
+ with chdir(dname):
p = psutil.Process()
cwd = p.cwd()
self.assertIsInstance(p.cwd(), str)
if self.expect_exact_path_match():
- self.assertEqual(cwd, self.funky_name)
+ self.assertEqual(cwd, dname)
def test_proc_open_files(self):
p = psutil.Process()
start = set(p.open_files())
- with open(self.funky_name, 'wb'):
+ with open(self.funky_name, 'rb'):
new = set(p.open_files())
path = (new - start).pop().path
self.assertIsInstance(path, str)
@@ -260,8 +264,10 @@ class _BaseFSAPIsTests(object):
self.assertEqual(conn.laddr, name)
def test_disk_usage(self):
- safe_mkdir(self.funky_name)
- psutil.disk_usage(self.funky_name)
+ dname = self.funky_name + "2"
+ self.addCleanup(safe_rmpath, dname)
+ safe_mkdir(dname)
+ psutil.disk_usage(dname)
@unittest.skipIf(not HAS_MEMORY_MAPS, "not supported")
@unittest.skipIf(not PY3, "ctypes does not support unicode on PY2")
@@ -335,6 +341,9 @@ class TestWinProcessName(unittest.TestCase):
class TestNonFSAPIS(unittest.TestCase):
"""Unicode tests for non fs-related APIs."""
+ def tearDown(self):
+ reap_children()
+
@unittest.skipIf(not HAS_ENVIRON, "not supported")
def test_proc_environ(self):
# Note: differently from others, this test does not deal