summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2015-11-12 02:46:59 -0800
committerGiampaolo Rodola <g.rodola@gmail.com>2015-11-12 02:46:59 -0800
commita7f4fe2dfd14e53244dcb9fa75ef6093432762c2 (patch)
tree434f04cbf95c47ecee9c57a8de7c4065bfa8740f
parente079f3c8feeeec70c1ea32491cf39721c1c82d15 (diff)
parentd4becc3f156bdc7565a64061f1c5711eb9f6791a (diff)
downloadpsutil-a7f4fe2dfd14e53244dcb9fa75ef6093432762c2.tar.gz
Merge pull request #709 from giampaolo/landryb-openbsd
Landryb openbsd
-rw-r--r--TODO2
-rw-r--r--docs/index.rst7
-rw-r--r--psutil/__init__.py64
-rw-r--r--psutil/_compat.py53
-rw-r--r--psutil/_psbsd.py166
-rw-r--r--psutil/_psutil_bsd.h20
-rw-r--r--psutil/_psutil_openbsd.c2419
-rw-r--r--psutil/_psutil_posix.c14
-rw-r--r--psutil/arch/bsd/openbsd.c761
-rw-r--r--psutil/arch/bsd/openbsd.h28
-rw-r--r--psutil/arch/bsd/process_info.h3
-rw-r--r--setup.py13
-rw-r--r--test/_freebsd.py (renamed from test/_bsd.py)10
-rw-r--r--test/test_psutil.py140
14 files changed, 3556 insertions, 144 deletions
diff --git a/TODO b/TODO
index a428da18..4a5e0f16 100644
--- a/TODO
+++ b/TODO
@@ -35,6 +35,8 @@ HIGHER PRIORITY
* Process.threads(): thread names; patch for OSX available at:
https://code.google.com/p/plcrashreporter/issues/detail?id=65
+ Sample code:
+ https://github.com/janmojzis/pstree/blob/master/proc_kvm.c
* Asynchronous psutil.Popen (see http://bugs.python.org/issue1191964)
diff --git a/docs/index.rst b/docs/index.rst
index 31ba1fc3..9561b45c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -892,7 +892,8 @@ Process class
.. method:: threads()
Return threads opened by process as a list of namedtuples including thread
- id and thread CPU times (user/system).
+ id and thread CPU times (user/system). On OpenBSD this method requires
+ root access.
.. method:: cpu_times()
@@ -959,7 +960,7 @@ Process class
>>> p.cpu_affinity(all_cpus)
>>>
- Availability: Linux, Windows, BSD
+ Availability: Linux, Windows, FreeBSD
.. versionchanged:: 2.2.0 added support for FreeBSD
@@ -1042,6 +1043,8 @@ Process class
...]
>>>
+ Availability: All platforms except OpenBSD.
+
.. method:: children(recursive=False)
Return the children of this process as a list of :Class:`Process` objects,
diff --git a/psutil/__init__.py b/psutil/__init__.py
index fa654771..9e68d5b0 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -122,7 +122,7 @@ elif sys.platform.startswith("win32"):
elif sys.platform.startswith("darwin"):
from . import _psosx as _psplatform
-elif sys.platform.startswith("freebsd"):
+elif sys.platform.startswith("freebsd") or sys.platform.startswith("openbsd"):
from . import _psbsd as _psplatform
elif sys.platform.startswith("sunos"):
@@ -167,6 +167,7 @@ AF_LINK = _psplatform.AF_LINK
_TOTAL_PHYMEM = None
_POSIX = os.name == 'posix'
_WINDOWS = os.name == 'nt'
+_OPENBSD = sys.platform.startswith("openbsd")
_timer = getattr(time, 'monotonic', time.time)
@@ -715,7 +716,7 @@ class Process(object):
else:
return self._proc.rlimit(resource, limits)
- # Windows, Linux and BSD only
+ # Windows, Linux and FreeBSD only
if hasattr(_psplatform.Process, "cpu_affinity_get"):
def cpu_affinity(self, cpus=None):
@@ -754,6 +755,7 @@ class Process(object):
"""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()
@@ -961,32 +963,33 @@ class Process(object):
except ZeroDivisionError:
return 0.0
- def memory_maps(self, grouped=True):
- """Return process' mapped memory regions as a list of namedtuples
- whose fields are variable depending on the platform.
+ if not _OPENBSD:
+ def memory_maps(self, grouped=True):
+ """Return process' mapped memory regions as a list of namedtuples
+ whose fields are variable depending on the platform.
- If 'grouped' is True the mapped regions with the same 'path'
- are grouped together and the different memory fields are summed.
+ If 'grouped' is True the mapped regions with the same 'path'
+ are grouped together and the different memory fields are summed.
- If 'grouped' is False every mapped region is shown as a single
- entity and the namedtuple will also include the mapped region's
- address space ('addr') and permission set ('perms').
- """
- it = self._proc.memory_maps()
- if grouped:
- d = {}
- for tupl in it:
- path = tupl[2]
- nums = tupl[3:]
- try:
- d[path] = map(lambda x, y: x + y, d[path], nums)
- except KeyError:
- d[path] = nums
- nt = _psplatform.pmmap_grouped
- return [nt(path, *d[path]) for path in d] # NOQA
- else:
- nt = _psplatform.pmmap_ext
- return [nt(*x) for x in it]
+ If 'grouped' is False every mapped region is shown as a single
+ entity and the namedtuple will also include the mapped region's
+ address space ('addr') and permission set ('perms').
+ """
+ it = self._proc.memory_maps()
+ if grouped:
+ d = {}
+ for tupl in it:
+ path = tupl[2]
+ nums = tupl[3:]
+ try:
+ d[path] = map(lambda x, y: x + y, d[path], nums)
+ except KeyError:
+ d[path] = nums
+ nt = _psplatform.pmmap_grouped
+ return [nt(path, *d[path]) for path in d] # NOQA
+ else:
+ nt = _psplatform.pmmap_ext
+ return [nt(*x) for x in it]
def open_files(self):
"""Return files opened by process as a list of
@@ -1028,8 +1031,13 @@ class Process(object):
os.kill(self.pid, sig)
except OSError as err:
if err.errno == errno.ESRCH:
- self._gone = True
- raise NoSuchProcess(self.pid, self._name)
+ if _OPENBSD and pid_exists(self.pid):
+ # We do this because os.kill() lies in case of
+ # zombie processes.
+ raise ZombieProcess(self.pid, self._name, self._ppid)
+ else:
+ self._gone = True
+ raise NoSuchProcess(self.pid, self._name)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
diff --git a/psutil/_compat.py b/psutil/_compat.py
index 9b743753..f79af3ac 100644
--- a/psutil/_compat.py
+++ b/psutil/_compat.py
@@ -6,6 +6,7 @@
import collections
import functools
+import os
import sys
__all__ = ["PY3", "long", "xrange", "unicode", "callable", "lru_cache"]
@@ -191,3 +192,55 @@ except ImportError:
return functools.update_wrapper(wrapper, user_function)
return decorating_function
+
+
+# python 3.3
+try:
+ from shutil import which
+except ImportError:
+ def which(cmd, mode=os.F_OK | os.X_OK, path=None):
+ """Given a command, mode, and a PATH string, return the path which
+ conforms to the given mode on the PATH, or None if there is no such
+ file.
+
+ `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
+ of os.environ.get("PATH"), or can be overridden with a custom search
+ path.
+ """
+ def _access_check(fn, mode):
+ return (os.path.exists(fn) and os.access(fn, mode)
+ and not os.path.isdir(fn))
+
+ if os.path.dirname(cmd):
+ if _access_check(cmd, mode):
+ return cmd
+ return None
+
+ if path is None:
+ path = os.environ.get("PATH", os.defpath)
+ if not path:
+ return None
+ path = path.split(os.pathsep)
+
+ if sys.platform == "win32":
+ if os.curdir not in path:
+ path.insert(0, os.curdir)
+
+ pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
+ if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
+ files = [cmd]
+ else:
+ files = [cmd + ext for ext in pathext]
+ else:
+ files = [cmd]
+
+ seen = set()
+ for dir in path:
+ normdir = os.path.normcase(dir)
+ if normdir not in seen:
+ seen.add(normdir)
+ for thefile in files:
+ name = os.path.join(dir, thefile)
+ if _access_check(name, mode):
+ return name
+ return None
diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py
index 82bf91f5..57d847b9 100644
--- a/psutil/_psbsd.py
+++ b/psutil/_psbsd.py
@@ -7,6 +7,7 @@
import errno
import functools
import os
+import sys
import xml.etree.ElementTree as ET
from collections import namedtuple
@@ -18,21 +19,47 @@ from ._common import conn_tmap
from ._common import sockfam_to_enum
from ._common import socktype_to_enum
from ._common import usage_percent
+from ._compat import which
__extra__all__ = []
# --- constants
-PROC_STATUSES = {
- cext.SIDL: _common.STATUS_IDLE,
- cext.SRUN: _common.STATUS_RUNNING,
- cext.SSLEEP: _common.STATUS_SLEEPING,
- cext.SSTOP: _common.STATUS_STOPPED,
- cext.SZOMB: _common.STATUS_ZOMBIE,
- cext.SWAIT: _common.STATUS_WAITING,
- cext.SLOCK: _common.STATUS_LOCKED,
-}
+FREEBSD = sys.platform.startswith("freebsd")
+OPENBSD = sys.platform.startswith("openbsd")
+
+if FREEBSD:
+ PROC_STATUSES = {
+ cext.SIDL: _common.STATUS_IDLE,
+ cext.SRUN: _common.STATUS_RUNNING,
+ cext.SSLEEP: _common.STATUS_SLEEPING,
+ cext.SSTOP: _common.STATUS_STOPPED,
+ cext.SZOMB: _common.STATUS_ZOMBIE,
+ cext.SWAIT: _common.STATUS_WAITING,
+ cext.SLOCK: _common.STATUS_LOCKED,
+ }
+elif OPENBSD:
+ PROC_STATUSES = {
+ cext.SIDL: _common.STATUS_IDLE,
+ cext.SSLEEP: _common.STATUS_SLEEPING,
+ cext.SSTOP: _common.STATUS_STOPPED,
+ # According to /usr/include/sys/proc.h SZOMB is unused.
+ # test_zombie_process() shows that SDEAD is the right
+ # equivalent. Also it appears there's no equivalent of
+ # psutil.STATUS_DEAD. SDEAD really means STATUS_ZOMBIE.
+ # cext.SZOMB: _common.STATUS_ZOMBIE,
+ cext.SDEAD: _common.STATUS_ZOMBIE,
+ # From http://www.eecs.harvard.edu/~margo/cs161/videos/proc.h.txt
+ # OpenBSD has SRUN and SONPROC: SRUN indicates that a process
+ # is runnable but *not* yet running, i.e. is on a run queue.
+ # SONPROC indicates that the process is actually executing on
+ # a CPU, i.e. it is no longer on a run queue.
+ # As such we'll map SRUN to STATUS_WAKING and SONPROC to
+ # STATUS_RUNNING
+ cext.SRUN: _common.STATUS_WAKING,
+ cext.SONPROC: _common.STATUS_RUNNING,
+ }
TCP_STATUSES = {
cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
@@ -84,6 +111,8 @@ def virtual_memory():
def swap_memory():
"""System swap memory as (total, used, free, sin, sout) namedtuple."""
+ if OPENBSD:
+ PAGESIZE = 1
total, used, free, sin, sout = [x * PAGESIZE for x in cext.swap_mem()]
percent = usage_percent(used, total, _round=1)
return _common.sswap(total, used, free, percent, sin, sout)
@@ -135,6 +164,8 @@ def cpu_count_physical():
# We may get None in case "sysctl kern.sched.topology_spec"
# is not supported on this BSD version, in which case we'll mimic
# os.cpu_count() and return None.
+ if sys.platform.startswith("openbsd"):
+ return cext.cpu_count_logical()
ret = None
s = cext.cpu_count_phys()
if s is not None:
@@ -189,6 +220,20 @@ def users():
def net_connections(kind):
+ if OPENBSD:
+ ret = []
+ for pid in pids():
+ try:
+ cons = Process(pid).connections(kind)
+ except (NoSuchProcess, ZombieProcess):
+ continue
+ else:
+ for conn in cons:
+ conn = list(conn)
+ conn.append(pid)
+ ret.append(_common.sconn(*conn))
+ return ret
+
if kind not in _common.conn_tmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in conn_tmap])))
@@ -226,8 +271,20 @@ def net_if_stats():
return ret
+if OPENBSD:
+ def pid_exists(pid):
+ exists = _psposix.pid_exists(pid)
+ if not exists:
+ # We do this because _psposix.pid_exists() lies in case of
+ # zombie processes.
+ return pid in pids()
+ else:
+ return True
+else:
+ pid_exists = _psposix.pid_exists
+
+
pids = cext.pids
-pid_exists = _psposix.pid_exists
disk_usage = _psposix.disk_usage
net_io_counters = cext.net_io_counters
disk_io_counters = cext.disk_io_counters
@@ -274,10 +331,24 @@ class Process(object):
@wrap_exceptions
def exe(self):
- return cext.proc_exe(self.pid)
+ if FREEBSD:
+ return cext.proc_exe(self.pid)
+ else:
+ # exe cannot be determined on OpenBSD; references:
+ # https://chromium.googlesource.com/chromium/src/base/+/
+ # master/base_paths_posix.cc
+ # We try our best guess by using which against the first
+ # cmdline arg (may return None).
+ cmdline = self.cmdline()
+ if cmdline:
+ return which(cmdline[0])
+ else:
+ return ""
@wrap_exceptions
def cmdline(self):
+ if OPENBSD and self.pid == 0:
+ return None # ...else it crashes
return cext.proc_cmdline(self.pid)
@wrap_exceptions
@@ -323,7 +394,11 @@ class Process(object):
@wrap_exceptions
def num_threads(self):
- return cext.proc_num_threads(self.pid)
+ if hasattr(cext, "proc_num_threads"):
+ # FreeBSD
+ return cext.proc_num_threads(self.pid)
+ else:
+ return len(self.threads())
@wrap_exceptions
def num_ctx_switches(self):
@@ -331,11 +406,17 @@ class Process(object):
@wrap_exceptions
def threads(self):
+ # Note: on OpenSBD this (/dev/mem) requires root access.
rawlist = cext.proc_threads(self.pid)
retlist = []
for thread_id, utime, stime in rawlist:
ntuple = _common.pthread(thread_id, utime, stime)
retlist.append(ntuple)
+ if OPENBSD:
+ # On OpenBSD the underlying C function does not raise NSP
+ # in case the process is gone (and the returned list may
+ # incomplete).
+ self.name() # raise NSP if the process disappeared on us
return retlist
@wrap_exceptions
@@ -353,6 +434,11 @@ class Process(object):
status = TCP_STATUSES[status]
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
ret.append(nt)
+ if OPENBSD:
+ # On OpenBSD the underlying C function does not raise NSP
+ # in case the process is gone (and the returned list may
+ # incomplete).
+ self.name() # raise NSP if the process disappeared on us
return ret
@wrap_exceptions
@@ -406,6 +492,8 @@ class Process(object):
"""Return process current working directory."""
# sometimes we get an empty string, in which case we turn
# it into None
+ if OPENBSD and self.pid == 0:
+ return None # ...else raises EINVAL
return cext.proc_cwd(self.pid) or None
@wrap_exceptions
@@ -426,30 +514,32 @@ class Process(object):
memory_maps = _not_implemented
num_fds = _not_implemented
- @wrap_exceptions
- def cpu_affinity_get(self):
- return cext.proc_cpu_affinity_get(self.pid)
+ if FREEBSD:
+ @wrap_exceptions
+ def cpu_affinity_get(self):
+ return cext.proc_cpu_affinity_get(self.pid)
- @wrap_exceptions
- def cpu_affinity_set(self, cpus):
- # Pre-emptively check if CPUs are valid because the C
- # function has a weird behavior in case of invalid CPUs,
- # see: https://github.com/giampaolo/psutil/issues/586
- allcpus = tuple(range(len(per_cpu_times())))
- for cpu in cpus:
- if cpu not in allcpus:
- raise ValueError("invalid CPU #%i (choose between %s)"
- % (cpu, allcpus))
- try:
- cext.proc_cpu_affinity_set(self.pid, cpus)
- except OSError as err:
- # 'man cpuset_setaffinity' about EDEADLK:
- # <<the call would leave a thread without a valid CPU to run
- # on because the set does not overlap with the thread's
- # anonymous mask>>
- if err.errno in (errno.EINVAL, errno.EDEADLK):
- for cpu in cpus:
- if cpu not in allcpus:
- raise ValueError("invalid CPU #%i (choose between %s)"
- % (cpu, allcpus))
- raise
+ @wrap_exceptions
+ def cpu_affinity_set(self, cpus):
+ # Pre-emptively check if CPUs are valid because the C
+ # function has a weird behavior in case of invalid CPUs,
+ # see: https://github.com/giampaolo/psutil/issues/586
+ allcpus = tuple(range(len(per_cpu_times())))
+ for cpu in cpus:
+ if cpu not in allcpus:
+ raise ValueError("invalid CPU #%i (choose between %s)"
+ % (cpu, allcpus))
+ try:
+ cext.proc_cpu_affinity_set(self.pid, cpus)
+ except OSError as err:
+ # 'man cpuset_setaffinity' about EDEADLK:
+ # <<the call would leave a thread without a valid CPU to run
+ # on because the set does not overlap with the thread's
+ # anonymous mask>>
+ if err.errno in (errno.EINVAL, errno.EDEADLK):
+ for cpu in cpus:
+ if cpu not in allcpus:
+ raise ValueError(
+ "invalid CPU #%i (choose between %s)" % (
+ cpu, allcpus))
+ raise
diff --git a/psutil/_psutil_bsd.h b/psutil/_psutil_bsd.h
index 803957da..41d548be 100644
--- a/psutil/_psutil_bsd.h
+++ b/psutil/_psutil_bsd.h
@@ -9,7 +9,6 @@
// --- per-process functions
static PyObject* psutil_proc_cmdline(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_connections(PyObject* self, PyObject* args);
static PyObject* psutil_proc_cpu_times(PyObject* self, PyObject* args);
static PyObject* psutil_proc_create_time(PyObject* self, PyObject* args);
static PyObject* psutil_proc_exe(PyObject* self, PyObject* args);
@@ -19,11 +18,9 @@ static PyObject* psutil_proc_memory_info(PyObject* self, PyObject* args);
static PyObject* psutil_proc_memory_maps(PyObject* self, PyObject* args);
static PyObject* psutil_proc_name(PyObject* self, PyObject* args);
static PyObject* psutil_proc_num_ctx_switches(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_num_fds(PyObject* self, PyObject* args);
static PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args);
static PyObject* psutil_proc_ppid(PyObject* self, PyObject* args);
static PyObject* psutil_proc_status(PyObject* self, PyObject* args);
-static PyObject* psutil_proc_threads(PyObject* self, PyObject* args);
static PyObject* psutil_proc_tty_nr(PyObject* self, PyObject* args);
static PyObject* psutil_proc_uids(PyObject* self, PyObject* args);
static PyObject* psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args);
@@ -31,8 +28,19 @@ static PyObject* psutil_proc_cpu_affinity_set(PyObject* self, PyObject* args);
#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
static PyObject* psutil_proc_open_files(PyObject* self, PyObject* args);
+#endif
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
static PyObject* psutil_proc_cwd(PyObject* self, PyObject* args);
#endif
+#ifdef __FreeBSD__
+static PyObject* psutil_proc_threads(PyObject* self, PyObject* args);
+#endif
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+static PyObject* psutil_proc_num_fds(PyObject* self, PyObject* args);
+#endif
+#ifdef __FreeBSD__
+static PyObject* psutil_proc_connections(PyObject* self, PyObject* args);
+#endif
// --- system-related functions
@@ -40,13 +48,15 @@ static PyObject* psutil_boot_time(PyObject* self, PyObject* args);
static PyObject* psutil_cpu_count_logical(PyObject* self, PyObject* args);
static PyObject* psutil_cpu_count_phys(PyObject* self, PyObject* args);
static PyObject* psutil_cpu_times(PyObject* self, PyObject* args);
-static PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args);
static PyObject* psutil_disk_partitions(PyObject* self, PyObject* args);
static PyObject* psutil_net_io_counters(PyObject* self, PyObject* args);
static PyObject* psutil_pids(PyObject* self, PyObject* args);
-static PyObject* psutil_swap_mem(PyObject* self, PyObject* args);
static PyObject* psutil_users(PyObject* self, PyObject* args);
+#ifdef __FreeBSD__
static PyObject* psutil_virtual_mem(PyObject* self, PyObject* args);
+static PyObject* psutil_swap_mem(PyObject* self, PyObject* args);
+static PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args);
+#endif
#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
static PyObject* psutil_per_cpu_times(PyObject* self, PyObject* args);
diff --git a/psutil/_psutil_openbsd.c b/psutil/_psutil_openbsd.c
new file mode 100644
index 00000000..9c979306
--- /dev/null
+++ b/psutil/_psutil_openbsd.c
@@ -0,0 +1,2419 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil (OpenBSD).
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Platform-specific module methods for FreeBSD and OpenBSD.
+
+ * OpenBSD references:
+ * - OpenBSD source code: http://anoncvs.spacehopper.org/openbsd-src/
+ *
+ * OpenBSD: missing compared to FreeBSD implementation:
+ * - psutil.net_connections()
+ * - psutil.Process.get/set_cpu_affinity() (not supported natively)
+ * - psutil.Process.memory_maps()
+ */
+
+
+#include <Python.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <sys/socketvar.h> // for struct xsocket
+#include <sys/un.h>
+#include <sys/unpcb.h>
+// for xinpcb struct
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h> // for struct xtcpcb
+#include <netinet/tcp_fsm.h> // for TCP connection states
+#include <arpa/inet.h> // for inet_ntop()
+
+#include <sys/mount.h>
+
+#include <net/if.h> // net io counters
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h> // process open files/connections
+#include <sys/un.h>
+
+#include "_psutil_bsd.h"
+#include "_psutil_common.h"
+
+#ifdef __FreeBSD__
+ #include "arch/bsd/process_info.h"
+#elif __OpenBSD__
+ #include "arch/bsd/openbsd.h"
+#endif
+
+#ifdef __FreeBSD__
+ #include <sys/cpuset.h>
+ #include <net/if_media.h>
+ #include <devstat.h> // get io counters
+ #include <sys/vmmeter.h> // needed for vmtotal struct
+ #include <libutil.h> // process open files, shared libs (kinfo_getvmmap)
+
+ #if __FreeBSD_version < 900000
+ #include <utmp.h> // system users
+ #else
+ #include <utmpx.h>
+ #endif
+#endif
+
+#ifdef __OpenBSD__
+ #include <utmp.h>
+ #include <sys/vnode.h> // for VREG
+ #define _KERNEL // for DTYPE_VNODE
+ #include <sys/file.h>
+ #undef _KERNEL
+ #include <sys/sched.h> // for CPUSTATES & CP_*
+#endif
+
+
+#define TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
+
+#ifdef __FreeBSD__
+ // convert a timeval struct to a double
+ // convert a bintime struct to milliseconds
+ #define BT2MSEC(bt) (bt.sec * 1000 + ( ( (uint64_t) 1000000000 * (uint32_t) (bt.frac >> 32) ) >> 32 ) / 1000000)
+#endif
+
+#ifdef __OpenBSD__
+ #define KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
+#endif
+
+
+#ifdef __FreeBSD__
+/*
+ * Utility function which fills a kinfo_proc struct based on process pid
+ */
+static int
+psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) {
+ int mib[4];
+ size_t size;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = pid;
+
+ size = sizeof(struct kinfo_proc);
+
+ if (sysctl((int *)mib, 4, proc, &size, NULL, 0) == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+
+ // sysctl stores 0 in the size if we can't find the process information.
+ if (size == 0) {
+ NoSuchProcess();
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+
+#ifdef __FreeBSD__
+/*
+ * Set exception to AccessDenied if pid exists else NoSuchProcess.
+ */
+void
+psutil_raise_ad_or_nsp(long pid) {
+ int ret;
+ ret = psutil_pid_exists(pid);
+ if (ret == 0)
+ NoSuchProcess();
+ else if (ret == 1)
+ AccessDenied();
+ else
+ return NULL;
+}
+#endif
+
+
+/*
+ * Return a Python list of all the PIDs running on the system.
+ */
+static PyObject *
+psutil_pids(PyObject *self, PyObject *args) {
+ kinfo_proc *proclist = NULL;
+ kinfo_proc *orig_address = NULL;
+ size_t num_processes;
+ size_t idx;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_pid = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (psutil_get_proc_list(&proclist, &num_processes) != 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "failed to retrieve process list.");
+ goto error;
+ }
+
+ if (num_processes > 0) {
+ orig_address = proclist; // save so we can free it after we're done
+ for (idx = 0; idx < num_processes; idx++) {
+#ifdef __FreeBSD__
+ py_pid = Py_BuildValue("i", proclist->ki_pid);
+#elif __OpenBSD__
+ py_pid = Py_BuildValue("i", proclist->p_pid);
+#endif
+ if (!py_pid)
+ goto error;
+ if (PyList_Append(py_retlist, py_pid))
+ goto error;
+ Py_DECREF(py_pid);
+ proclist++;
+ }
+ free(orig_address);
+ }
+
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_pid);
+ Py_DECREF(py_retlist);
+ if (orig_address != NULL)
+ free(orig_address);
+ return NULL;
+}
+
+
+/*
+ * Return a Python float indicating the system boot time expressed in
+ * seconds since the epoch.
+ */
+static PyObject *
+psutil_boot_time(PyObject *self, PyObject *args) {
+ // fetch sysctl "kern.boottime"
+ static int request[2] = { CTL_KERN, KERN_BOOTTIME };
+ struct timeval boottime;
+ size_t len = sizeof(boottime);
+
+ if (sysctl(request, 2, &boottime, &len, NULL, 0) == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ return Py_BuildValue("d", (double)boottime.tv_sec);
+}
+
+
+/*
+ * Return process name from kinfo_proc as a Python string.
+ */
+static PyObject *
+psutil_proc_name(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+#ifdef __FreeBSD__
+ return Py_BuildValue("s", kp.ki_comm);
+#elif __OpenBSD__
+ return Py_BuildValue("s", kp.p_comm);
+#endif
+}
+
+
+#ifdef __FreeBSD__
+/*
+ * Return process pathname executable.
+ * Thanks to Robert N. M. Watson:
+ * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT
+ */
+static PyObject *
+psutil_proc_exe(PyObject *self, PyObject *args) {
+ long pid;
+ char pathname[PATH_MAX];
+ int error;
+ int mib[4];
+ int ret;
+ size_t size;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = pid;
+
+ size = sizeof(pathname);
+ error = sysctl(mib, 4, pathname, &size, NULL, 0);
+ if (error == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ if (size == 0 || strlen(pathname) == 0) {
+ ret = psutil_pid_exists(pid);
+ if (ret == -1)
+ return NULL;
+ else if (ret == 0)
+ return NoSuchProcess();
+ else
+ strcpy(pathname, "");
+ }
+ return Py_BuildValue("s", pathname);
+}
+#endif
+
+
+/*
+ * Return process cmdline as a Python list of cmdline arguments.
+ */
+static PyObject *
+psutil_proc_cmdline(PyObject *self, PyObject *args) {
+ long pid;
+ PyObject *py_retlist = NULL;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+
+ py_retlist = psutil_get_cmdline(pid);
+ // psutil_get_cmdline() returns NULL only if psutil_cmd_args
+ // failed with ESRCH (no process with that PID)
+ if (NULL == py_retlist)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ return Py_BuildValue("N", py_retlist);
+}
+
+
+/*
+ * Return process parent pid from kinfo_proc as a Python integer.
+ */
+static PyObject *
+psutil_proc_ppid(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+#ifdef __FreeBSD__
+ return Py_BuildValue("l", (long)kp.ki_ppid);
+#elif __OpenBSD__
+ return Py_BuildValue("l", (long)kp.p_ppid);
+#endif
+}
+
+
+/*
+ * Return process status as a Python integer.
+ */
+static PyObject *
+psutil_proc_status(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+#ifdef __FreeBSD__
+ return Py_BuildValue("i", (int)kp.ki_stat);
+#elif __OpenBSD__
+ return Py_BuildValue("i", (int)kp.p_stat);
+#endif
+}
+
+
+/*
+ * Return process real, effective and saved user ids from kinfo_proc
+ * as a Python tuple.
+ */
+static PyObject *
+psutil_proc_uids(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+ return Py_BuildValue("lll",
+#ifdef __FreeBSD__
+ (long)kp.ki_ruid,
+ (long)kp.ki_uid,
+ (long)kp.ki_svuid);
+#elif __OpenBSD__
+ (long)kp.p_ruid,
+ (long)kp.p_uid,
+ (long)kp.p_svuid);
+#endif
+}
+
+
+/*
+ * Return process real, effective and saved group ids from kinfo_proc
+ * as a Python tuple.
+ */
+static PyObject *
+psutil_proc_gids(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+ return Py_BuildValue("lll",
+#ifdef __FreeBSD__
+ (long)kp.ki_rgid,
+ (long)kp.ki_groups[0],
+ (long)kp.ki_svuid);
+#elif __OpenBSD__
+ (long)kp.p_rgid,
+ (long)kp.p_groups[0],
+ (long)kp.p_svuid);
+#endif
+}
+
+
+/*
+ * Return process real, effective and saved group ids from kinfo_proc
+ * as a Python tuple.
+ */
+static PyObject *
+psutil_proc_tty_nr(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+#ifdef __FreeBSD__
+ return Py_BuildValue("i", kp.ki_tdev);
+#elif __OpenBSD__
+ return Py_BuildValue("i", kp.p_tdev);
+#endif
+}
+
+
+/*
+ * Return the number of context switches performed by process as a tuple.
+ */
+static PyObject *
+psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+ return Py_BuildValue("(ll)",
+#ifdef __FreeBSD__
+ kp.ki_rusage.ru_nvcsw,
+ kp.ki_rusage.ru_nivcsw);
+#elif __OpenBSD__
+ kp.p_uru_nvcsw,
+ kp.p_uru_nivcsw);
+#endif
+}
+
+
+#ifdef __FreeBSD__
+/*
+ * Return number of threads used by process as a Python integer.
+ */
+static PyObject *
+psutil_proc_num_threads(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+ return Py_BuildValue("l", (long)kp.ki_numthreads);
+}
+#endif
+
+
+/*
+ * Retrieves all threads used by process returning a list of tuples
+ * including thread id, user time and system time.
+ * Thanks to Robert N. M. Watson (FreeBSD):
+ * http://fxr.googlebit.com/source/usr.bin/procstat/
+ * procstat_threads.c?v=8-CURRENT
+ */
+
+#ifdef __FreeBSD__
+static PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+ long pid;
+ int mib[4];
+ struct kinfo_proc *kip = NULL;
+ struct kinfo_proc *kipp = NULL;
+ int error;
+ unsigned int i;
+ size_t size;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ goto error;
+
+ // we need to re-query for thread information, so don't use *kipp
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD;
+ mib[3] = pid;
+
+ size = 0;
+ error = sysctl(mib, 4, NULL, &size, NULL, 0);
+ if (error == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ if (size == 0) {
+ NoSuchProcess();
+ goto error;
+ }
+
+ kip = malloc(size);
+ if (kip == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ error = sysctl(mib, 4, kip, &size, NULL, 0);
+ if (error == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ if (size == 0) {
+ NoSuchProcess();
+ goto error;
+ }
+
+ for (i = 0; i < size / sizeof(*kipp); i++) {
+ kipp = &kip[i];
+ py_tuple = Py_BuildValue("Idd",
+ kipp->ki_tid,
+ TV2DOUBLE(kipp->ki_rusage.ru_utime),
+ TV2DOUBLE(kipp->ki_rusage.ru_stime));
+ if (py_tuple == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+ free(kip);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (kip != NULL)
+ free(kip);
+ return NULL;
+}
+#endif
+
+
+/*
+ * Return a Python tuple (user_time, kernel_time)
+ */
+static PyObject *
+psutil_proc_cpu_times(PyObject *self, PyObject *args) {
+ long pid;
+ double user_t, sys_t;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+ // convert from microseconds to seconds
+#ifdef __FreeBSD__
+ user_t = TV2DOUBLE(kp.ki_rusage.ru_utime);
+ sys_t = TV2DOUBLE(kp.ki_rusage.ru_stime);
+#elif __OpenBSD__
+ user_t = KPT2DOUBLE(kp.p_uutime);
+ sys_t = KPT2DOUBLE(kp.p_ustime);
+#endif
+ return Py_BuildValue("(dd)", user_t, sys_t);
+}
+
+
+/*
+ * Return the number of logical CPUs in the system.
+ * XXX this could be shared with OSX
+ */
+static PyObject *
+psutil_cpu_count_logical(PyObject *self, PyObject *args) {
+ int mib[2];
+ int ncpu;
+ size_t len;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ len = sizeof(ncpu);
+
+ if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1)
+ Py_RETURN_NONE; // mimic os.cpu_count()
+ else
+ return Py_BuildValue("i", ncpu);
+}
+
+
+#ifdef __FreeBSD__
+/*
+ * Return an XML string from which we'll determine the number of
+ * physical CPU cores in the system.
+ */
+static PyObject *
+psutil_cpu_count_phys(PyObject *self, PyObject *args) {
+ void *topology = NULL;
+ size_t size = 0;
+ PyObject *py_str;
+
+ if (sysctlbyname("kern.sched.topology_spec", NULL, &size, NULL, 0))
+ goto error;
+
+ topology = malloc(size);
+ if (!topology) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (sysctlbyname("kern.sched.topology_spec", topology, &size, NULL, 0))
+ goto error;
+
+ py_str = Py_BuildValue("s", topology);
+ free(topology);
+ return py_str;
+
+error:
+ if (topology != NULL)
+ free(topology);
+ Py_RETURN_NONE;
+}
+#endif
+
+
+/*
+ * Return a Python float indicating the process create time expressed in
+ * seconds since the epoch.
+ */
+static PyObject *
+psutil_proc_create_time(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+#ifdef __FreeBSD__
+ return Py_BuildValue("d", TV2DOUBLE(kp.ki_start));
+#elif __OpenBSD__
+ return Py_BuildValue("d", KPT2DOUBLE(kp.p_ustart));
+#endif
+}
+
+
+/*
+ * Return a Python float indicating the process create time expressed in
+ * seconds since the epoch.
+ */
+static PyObject *
+psutil_proc_io_counters(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+ // there's apparently no way to determine bytes count, hence return -1.
+ return Py_BuildValue("(llll)",
+#ifdef __FreeBSD__
+ kp.ki_rusage.ru_inblock,
+ kp.ki_rusage.ru_oublock,
+#elif __OpenBSD__
+ kp.p_uru_inblock,
+ kp.p_uru_oublock,
+#endif
+ -1,
+ -1);
+}
+
+
+#ifdef __OpenBSD__
+#define ptoa(x) ((paddr_t)(x) << PAGE_SHIFT)
+#endif
+
+/*
+ * Return extended memory info for a process as a Python tuple.
+ */
+static PyObject *
+psutil_proc_memory_info(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_proc kp;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+ return Py_BuildValue(
+ "(lllll)",
+#ifdef __FreeBSD__
+ ptoa(kp.ki_rssize), // rss
+ (long)kp.ki_size, // vms
+ ptoa(kp.ki_tsize), // text
+ ptoa(kp.ki_dsize), // data
+ ptoa(kp.ki_ssize)); // stack
+#elif __OpenBSD__
+ ptoa(kp.p_vm_rssize), // rss
+ // vms, this is how ps does it, see:
+ // http://anoncvs.spacehopper.org/openbsd-src/tree/bin/ps/print.c#n461
+ ptoa(kp.p_vm_dsize + kp.p_vm_ssize + kp.p_vm_tsize), // vms
+ ptoa(kp.p_vm_tsize), // text
+ ptoa(kp.p_vm_dsize), // data
+ ptoa(kp.p_vm_ssize)); // stack
+#endif
+}
+
+
+#ifdef __FreeBSD__
+/*
+ * Return virtual memory usage statistics.
+ */
+static PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+ unsigned int total, active, inactive, wired, cached, free;
+ size_t size = sizeof(total);
+ struct vmtotal vm;
+ int mib[] = {CTL_VM, VM_METER};
+ long pagesize = getpagesize();
+#if __FreeBSD_version > 702101
+ long buffers;
+#else
+ int buffers;
+#endif
+ size_t buffers_size = sizeof(buffers);
+
+ if (sysctlbyname("vm.stats.vm.v_page_count", &total, &size, NULL, 0))
+ goto error;
+ if (sysctlbyname("vm.stats.vm.v_active_count", &active, &size, NULL, 0))
+ goto error;
+ if (sysctlbyname("vm.stats.vm.v_inactive_count",
+ &inactive, &size, NULL, 0))
+ goto error;
+ if (sysctlbyname("vm.stats.vm.v_wire_count", &wired, &size, NULL, 0))
+ goto error;
+ if (sysctlbyname("vm.stats.vm.v_cache_count", &cached, &size, NULL, 0))
+ goto error;
+ if (sysctlbyname("vm.stats.vm.v_free_count", &free, &size, NULL, 0))
+ goto error;
+ if (sysctlbyname("vfs.bufspace", &buffers, &buffers_size, NULL, 0))
+ goto error;
+
+ size = sizeof(vm);
+ if (sysctl(mib, 2, &vm, &size, NULL, 0) != 0)
+ goto error;
+
+ return Py_BuildValue("KKKKKKKK",
+ (unsigned long long) total * pagesize,
+ (unsigned long long) free * pagesize,
+ (unsigned long long) active * pagesize,
+ (unsigned long long) inactive * pagesize,
+ (unsigned long long) wired * pagesize,
+ (unsigned long long) cached * pagesize,
+ (unsigned long long) buffers,
+ (unsigned long long) (vm.t_vmshr + vm.t_rmshr) * pagesize // shared
+ );
+
+error:
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+}
+#endif
+
+
+#ifndef _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#ifdef __FreeBSD__
+/*
+ * Return swap memory stats (see 'swapinfo' cmdline tool)
+ */
+static PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+ kvm_t *kd;
+ struct kvm_swap kvmsw[1];
+ unsigned int swapin, swapout, nodein, nodeout;
+ size_t size = sizeof(unsigned int);
+
+ kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open failed");
+ if (kd == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "kvm_open failed");
+ return NULL;
+ }
+
+ if (kvm_getswapinfo(kd, kvmsw, 1, 0) < 0) {
+ kvm_close(kd);
+ PyErr_SetString(PyExc_RuntimeError, "kvm_getswapinfo failed");
+ return NULL;
+ }
+
+ kvm_close(kd);
+
+ if (sysctlbyname("vm.stats.vm.v_swapin", &swapin, &size, NULL, 0) == -1)
+ goto sbn_error;
+ if (sysctlbyname("vm.stats.vm.v_swapout", &swapout, &size, NULL, 0) == -1)
+ goto sbn_error;
+ if (sysctlbyname("vm.stats.vm.v_vnodein", &nodein, &size, NULL, 0) == -1)
+ goto sbn_error;
+ if (sysctlbyname("vm.stats.vm.v_vnodeout", &nodeout, &size, NULL, 0) == -1)
+ goto sbn_error;
+
+ return Py_BuildValue("(iiiII)",
+ kvmsw[0].ksw_total, // total
+ kvmsw[0].ksw_used, // used
+ kvmsw[0].ksw_total - kvmsw[0].ksw_used, // free
+ swapin + swapout, // swap in
+ nodein + nodeout); // swap out
+
+sbn_error:
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+}
+#endif
+
+
+/*
+ * Return a Python tuple representing user, kernel and idle CPU times
+ */
+static PyObject *
+psutil_cpu_times(PyObject *self, PyObject *args) {
+ long cpu_time[CPUSTATES];
+ size_t size = sizeof(cpu_time);
+ int ret;
+
+#ifdef __FreeBSD__
+ ret = sysctlbyname("kern.cp_time", &cpu_time, &size, NULL, 0);
+#elif __OpenBSD__
+ int mib[] = {CTL_KERN, KERN_CPTIME};
+ ret = sysctl(mib, 2, &cpu_time, &size, NULL, 0);
+#endif
+ if (ret == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ return Py_BuildValue("(ddddd)",
+ (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,
+ (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,
+ (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,
+ (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,
+ (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC
+ );
+}
+
+
+ /*
+ * Return files opened by process as a list of (path, fd) tuples.
+ * TODO: this is broken as it may report empty paths. 'procstat'
+ * utility has the same problem see:
+ * https://github.com/giampaolo/psutil/issues/595
+ */
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 || __OpenBSD__
+static PyObject *
+psutil_proc_open_files(PyObject *self, PyObject *args) {
+ long pid;
+ int i, cnt;
+ struct kinfo_file *freep = NULL;
+ struct kinfo_file *kif;
+ struct kinfo_proc kipp;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ goto error;
+ if (psutil_kinfo_proc(pid, &kipp) == -1)
+ goto error;
+
+ freep = kinfo_getfile(pid, &cnt);
+ if (freep == NULL) {
+ psutil_raise_ad_or_nsp(pid);
+ goto error;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ kif = &freep[i];
+#ifdef __FreeBSD__
+ if ((kif->kf_type == KF_TYPE_VNODE) &&
+ (kif->kf_vnode_type == KF_VTYPE_VREG))
+ {
+ py_tuple = Py_BuildValue("(si)", kif->kf_path, kif->kf_fd);
+#else
+ if ((kif->f_type == DTYPE_VNODE) &&
+ (kif->v_type == VREG))
+ {
+ py_tuple = Py_BuildValue("(si)", "", kif->fd_fd);
+#endif
+ if (py_tuple == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+ }
+ free(freep);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (freep != NULL)
+ free(freep);
+ return NULL;
+}
+#endif
+
+
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+/*
+ * Return files opened by process as a list of (path, fd) tuples
+ */
+static PyObject *
+psutil_proc_num_fds(PyObject *self, PyObject *args) {
+ long pid;
+ int cnt;
+
+ struct kinfo_file *freep;
+ struct kinfo_proc kipp;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kipp) == -1)
+ return NULL;
+
+ freep = kinfo_getfile(pid, &cnt);
+ if (freep == NULL) {
+ psutil_raise_ad_or_nsp(pid);
+ return NULL;
+ }
+ free(freep);
+
+ return Py_BuildValue("i", cnt);
+}
+#endif
+
+
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+/*
+ * Return process current working directory.
+ */
+static PyObject *
+psutil_proc_cwd(PyObject *self, PyObject *args) {
+ long pid;
+ struct kinfo_file *freep = NULL;
+ struct kinfo_file *kif;
+ struct kinfo_proc kipp;
+ PyObject *py_path = NULL;
+
+ int i, cnt;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ goto error;
+ if (psutil_kinfo_proc(pid, &kipp) == -1)
+ goto error;
+
+ freep = kinfo_getfile(pid, &cnt);
+ if (freep == NULL) {
+ psutil_raise_ad_or_nsp(pid);
+ goto error;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ kif = &freep[i];
+ if (kif->kf_fd == KF_FD_TYPE_CWD) {
+ py_path = Py_BuildValue("s", kif->kf_path);
+ if (!py_path)
+ goto error;
+ break;
+ }
+ }
+ /*
+ * For lower pids it seems we can't retrieve any information
+ * (lsof can't do that it either). Since this happens even
+ * as root we return an empty string instead of AccessDenied.
+ */
+ if (py_path == NULL)
+ py_path = Py_BuildValue("s", "");
+ free(freep);
+ return py_path;
+
+error:
+ Py_XDECREF(py_path);
+ if (freep != NULL)
+ free(freep);
+ return NULL;
+}
+#endif
+
+
+#ifdef __FreeBSD__
+// The tcplist fetching and walking is borrowed from netstat/inet.c.
+static char *
+psutil_fetch_tcplist(void) {
+ char *buf;
+ size_t len;
+
+ for (;;) {
+ if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ buf = malloc(len);
+ if (buf == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ if (sysctlbyname("net.inet.tcp.pcblist", buf, &len, NULL, 0) < 0) {
+ free(buf);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ return buf;
+ }
+}
+
+static int
+psutil_sockaddr_port(int family, struct sockaddr_storage *ss) {
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin;
+
+ if (family == AF_INET) {
+ sin = (struct sockaddr_in *)ss;
+ return (sin->sin_port);
+ }
+ else {
+ sin6 = (struct sockaddr_in6 *)ss;
+ return (sin6->sin6_port);
+ }
+}
+
+static void *
+psutil_sockaddr_addr(int family, struct sockaddr_storage *ss) {
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin;
+
+ if (family == AF_INET) {
+ sin = (struct sockaddr_in *)ss;
+ return (&sin->sin_addr);
+ }
+ else {
+ sin6 = (struct sockaddr_in6 *)ss;
+ return (&sin6->sin6_addr);
+ }
+}
+
+static socklen_t
+psutil_sockaddr_addrlen(int family) {
+ if (family == AF_INET)
+ return (sizeof(struct in_addr));
+ else
+ return (sizeof(struct in6_addr));
+}
+
+static int
+psutil_sockaddr_matches(int family, int port, void *pcb_addr,
+ struct sockaddr_storage *ss) {
+ if (psutil_sockaddr_port(family, ss) != port)
+ return (0);
+ return (memcmp(psutil_sockaddr_addr(family, ss), pcb_addr,
+ psutil_sockaddr_addrlen(family)) == 0);
+}
+
+static struct tcpcb *
+psutil_search_tcplist(char *buf, struct kinfo_file *kif) {
+ struct tcpcb *tp;
+ struct inpcb *inp;
+ struct xinpgen *xig, *oxig;
+ struct xsocket *so;
+
+ oxig = xig = (struct xinpgen *)buf;
+ for (xig = (struct xinpgen *)((char *)xig + xig->xig_len);
+ xig->xig_len > sizeof(struct xinpgen);
+ xig = (struct xinpgen *)((char *)xig + xig->xig_len)) {
+ tp = &((struct xtcpcb *)xig)->xt_tp;
+ inp = &((struct xtcpcb *)xig)->xt_inp;
+ so = &((struct xtcpcb *)xig)->xt_socket;
+
+ if (so->so_type != kif->kf_sock_type ||
+ so->xso_family != kif->kf_sock_domain ||
+ so->xso_protocol != kif->kf_sock_protocol)
+ continue;
+
+ if (kif->kf_sock_domain == AF_INET) {
+ if (!psutil_sockaddr_matches(
+ AF_INET, inp->inp_lport, &inp->inp_laddr,
+ &kif->kf_sa_local))
+ continue;
+ if (!psutil_sockaddr_matches(
+ AF_INET, inp->inp_fport, &inp->inp_faddr,
+ &kif->kf_sa_peer))
+ continue;
+ } else {
+ if (!psutil_sockaddr_matches(
+ AF_INET6, inp->inp_lport, &inp->in6p_laddr,
+ &kif->kf_sa_local))
+ continue;
+ if (!psutil_sockaddr_matches(
+ AF_INET6, inp->inp_fport, &inp->in6p_faddr,
+ &kif->kf_sa_peer))
+ continue;
+ }
+
+ return (tp);
+ }
+ return NULL;
+}
+
+
+// a signaler for connections without an actual status
+static int PSUTIL_CONN_NONE = 128;
+
+/*
+ * Return connections opened by process.
+ */
+static PyObject *
+psutil_proc_connections(PyObject *self, PyObject *args) {
+ long pid;
+ int i, cnt;
+
+ struct kinfo_file *freep = NULL;
+ struct kinfo_file *kif;
+ char *tcplist = NULL;
+ struct tcpcb *tcp;
+
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_laddr = NULL;
+ PyObject *py_raddr = NULL;
+ PyObject *py_af_filter = NULL;
+ PyObject *py_type_filter = NULL;
+ PyObject *py_family = NULL;
+ PyObject *py_type = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter))
+ goto error;
+ if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {
+ PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
+ goto error;
+ }
+
+ freep = kinfo_getfile(pid, &cnt);
+ if (freep == NULL) {
+ psutil_raise_ad_or_nsp(pid);
+ goto error;
+ }
+
+ tcplist = psutil_fetch_tcplist();
+ if (tcplist == NULL) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ int lport, rport, state;
+ char lip[200], rip[200];
+ char path[PATH_MAX];
+ int inseq;
+ py_tuple = NULL;
+ py_laddr = NULL;
+ py_raddr = NULL;
+
+ kif = &freep[i];
+ if (kif->kf_type == KF_TYPE_SOCKET) {
+ // apply filters
+ py_family = PyLong_FromLong((long)kif->kf_sock_domain);
+ inseq = PySequence_Contains(py_af_filter, py_family);
+ Py_DECREF(py_family);
+ if (inseq == 0)
+ continue;
+ py_type = PyLong_FromLong((long)kif->kf_sock_type);
+ inseq = PySequence_Contains(py_type_filter, py_type);
+ Py_DECREF(py_type);
+ if (inseq == 0)
+ continue;
+ // IPv4 / IPv6 socket
+ if ((kif->kf_sock_domain == AF_INET) ||
+ (kif->kf_sock_domain == AF_INET6)) {
+ // fill status
+ state = PSUTIL_CONN_NONE;
+ if (kif->kf_sock_type == SOCK_STREAM) {
+ tcp = psutil_search_tcplist(tcplist, kif);
+ if (tcp != NULL)
+ state = (int)tcp->t_state;
+ }
+
+ // build addr and port
+ inet_ntop(
+ kif->kf_sock_domain,
+ psutil_sockaddr_addr(kif->kf_sock_domain,
+ &kif->kf_sa_local),
+ lip,
+ sizeof(lip));
+ inet_ntop(
+ kif->kf_sock_domain,
+ psutil_sockaddr_addr(kif->kf_sock_domain,
+ &kif->kf_sa_peer),
+ rip,
+ sizeof(rip));
+ lport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
+ &kif->kf_sa_local));
+ rport = htons(psutil_sockaddr_port(kif->kf_sock_domain,
+ &kif->kf_sa_peer));
+
+ // construct python tuple/list
+ py_laddr = Py_BuildValue("(si)", lip, lport);
+ if (!py_laddr)
+ goto error;
+ if (rport != 0)
+ py_raddr = Py_BuildValue("(si)", rip, rport);
+ else
+ py_raddr = Py_BuildValue("()");
+ if (!py_raddr)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(iiiNNi)",
+ kif->kf_fd,
+ kif->kf_sock_domain,
+ kif->kf_sock_type,
+ py_laddr,
+ py_raddr,
+ state
+ );
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+ // UNIX socket
+ else if (kif->kf_sock_domain == AF_UNIX) {
+ struct sockaddr_un *sun;
+
+ sun = (struct sockaddr_un *)&kif->kf_sa_local;
+ snprintf(
+ path, sizeof(path), "%.*s",
+ (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
+ sun->sun_path);
+
+ py_tuple = Py_BuildValue(
+ "(iiisOi)",
+ kif->kf_fd,
+ kif->kf_sock_domain,
+ kif->kf_sock_type,
+ path,
+ Py_None,
+ PSUTIL_CONN_NONE
+ );
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ Py_INCREF(Py_None);
+ }
+ }
+ }
+ free(freep);
+ free(tcplist);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_laddr);
+ Py_XDECREF(py_raddr);
+ Py_DECREF(py_retlist);
+ if (freep != NULL)
+ free(freep);
+ if (tcplist != NULL)
+ free(tcplist);
+ return NULL;
+}
+#endif
+
+
+#ifdef __FreeBSD__
+static PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+ static int maxcpus;
+ int mib[2];
+ int ncpu;
+ size_t len;
+ size_t size;
+ int i;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_cputime = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ // retrieve maxcpus value
+ size = sizeof(maxcpus);
+ if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) {
+ Py_DECREF(py_retlist);
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ long cpu_time[maxcpus][CPUSTATES];
+
+ // retrieve the number of cpus
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ len = sizeof(ncpu);
+ if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ // per-cpu info
+ size = sizeof(cpu_time);
+ if (sysctlbyname("kern.cp_times", &cpu_time, &size, NULL, 0) == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < ncpu; i++) {
+ py_cputime = Py_BuildValue(
+ "(ddddd)",
+ (double)cpu_time[i][CP_USER] / CLOCKS_PER_SEC,
+ (double)cpu_time[i][CP_NICE] / CLOCKS_PER_SEC,
+ (double)cpu_time[i][CP_SYS] / CLOCKS_PER_SEC,
+ (double)cpu_time[i][CP_IDLE] / CLOCKS_PER_SEC,
+ (double)cpu_time[i][CP_INTR] / CLOCKS_PER_SEC);
+ if (!py_cputime)
+ goto error;
+ if (PyList_Append(py_retlist, py_cputime))
+ goto error;
+ Py_DECREF(py_cputime);
+ }
+
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_cputime);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+#endif
+
+
+#ifdef __FreeBSD__
+// remove spaces from string
+void remove_spaces(char *str) {
+ char *p1 = str;
+ char *p2 = str;
+ do
+ while (*p2 == ' ')
+ p2++;
+ while (*p1++ = *p2++);
+}
+
+
+/*
+ * Return a list of tuples for every process memory maps.
+ * 'procstat' cmdline utility has been used as an example.
+ */
+static PyObject *
+psutil_proc_memory_maps(PyObject *self, PyObject *args) {
+ long pid;
+ int ptrwidth;
+ int i, cnt;
+ char addr[1000];
+ char perms[4];
+ const char *path;
+ struct kinfo_proc kp;
+ struct kinfo_vmentry *freep = NULL;
+ struct kinfo_vmentry *kve;
+ ptrwidth = 2 * sizeof(void *);
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ goto error;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ goto error;
+
+ freep = kinfo_getvmmap(pid, &cnt);
+ if (freep == NULL) {
+ psutil_raise_ad_or_nsp(pid);
+ goto error;
+ }
+ for (i = 0; i < cnt; i++) {
+ py_tuple = NULL;
+ kve = &freep[i];
+ addr[0] = '\0';
+ perms[0] = '\0';
+ sprintf(addr, "%#*jx-%#*jx", ptrwidth, (uintmax_t)kve->kve_start,
+ ptrwidth, (uintmax_t)kve->kve_end);
+ remove_spaces(addr);
+ strlcat(perms, kve->kve_protection & KVME_PROT_READ ? "r" : "-",
+ sizeof(perms));
+ strlcat(perms, kve->kve_protection & KVME_PROT_WRITE ? "w" : "-",
+ sizeof(perms));
+ strlcat(perms, kve->kve_protection & KVME_PROT_EXEC ? "x" : "-",
+ sizeof(perms));
+
+ if (strlen(kve->kve_path) == 0) {
+ switch (kve->kve_type) {
+ case KVME_TYPE_NONE:
+ path = "[none]";
+ break;
+ case KVME_TYPE_DEFAULT:
+ path = "[default]";
+ break;
+ case KVME_TYPE_VNODE:
+ path = "[vnode]";
+ break;
+ case KVME_TYPE_SWAP:
+ path = "[swap]";
+ break;
+ case KVME_TYPE_DEVICE:
+ path = "[device]";
+ break;
+ case KVME_TYPE_PHYS:
+ path = "[phys]";
+ break;
+ case KVME_TYPE_DEAD:
+ path = "[dead]";
+ break;
+ case KVME_TYPE_SG:
+ path = "[sg]";
+ break;
+ case KVME_TYPE_UNKNOWN:
+ path = "[unknown]";
+ break;
+ default:
+ path = "[?]";
+ break;
+ }
+ }
+ else {
+ path = kve->kve_path;
+ }
+
+ py_tuple = Py_BuildValue("sssiiii",
+ addr, // "start-end" address
+ perms, // "rwx" permissions
+ path, // path
+ kve->kve_resident, // rss
+ kve->kve_private_resident, // private
+ kve->kve_ref_count, // ref count
+ kve->kve_shadow_count); // shadow count
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+ free(freep);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (freep != NULL)
+ free(freep);
+ return NULL;
+}
+#endif
+
+
+/*
+ * Return a list of tuples including device, mount point and fs type
+ * for all partitions mounted on the system.
+ */
+static PyObject *
+psutil_disk_partitions(PyObject *self, PyObject *args) {
+ int num;
+ int i;
+ long len;
+ uint64_t flags;
+ char opts[200];
+ struct statfs *fs = NULL;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ // get the number of mount points
+ Py_BEGIN_ALLOW_THREADS
+ num = getfsstat(NULL, 0, MNT_NOWAIT);
+ Py_END_ALLOW_THREADS
+ if (num == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ len = sizeof(*fs) * num;
+ fs = malloc(len);
+ if (fs == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ num = getfsstat(fs, len, MNT_NOWAIT);
+ Py_END_ALLOW_THREADS
+ if (num == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < num; i++) {
+ py_tuple = NULL;
+ opts[0] = 0;
+ flags = fs[i].f_flags;
+
+ // see sys/mount.h
+ if (flags & MNT_RDONLY)
+ strlcat(opts, "ro", sizeof(opts));
+ else
+ strlcat(opts, "rw", sizeof(opts));
+#ifdef __FreeBSD__
+ if (flags & MNT_SYNCHRONOUS)
+ strlcat(opts, ",sync", sizeof(opts));
+ if (flags & MNT_NOEXEC)
+ strlcat(opts, ",noexec", sizeof(opts));
+ if (flags & MNT_NOSUID)
+ strlcat(opts, ",nosuid", sizeof(opts));
+ if (flags & MNT_UNION)
+ strlcat(opts, ",union", sizeof(opts));
+ if (flags & MNT_ASYNC)
+ strlcat(opts, ",async", sizeof(opts));
+ if (flags & MNT_SUIDDIR)
+ strlcat(opts, ",suiddir", sizeof(opts));
+ if (flags & MNT_SOFTDEP)
+ strlcat(opts, ",softdep", sizeof(opts));
+ if (flags & MNT_NOSYMFOLLOW)
+ strlcat(opts, ",nosymfollow", sizeof(opts));
+ if (flags & MNT_GJOURNAL)
+ strlcat(opts, ",gjournal", sizeof(opts));
+ if (flags & MNT_MULTILABEL)
+ strlcat(opts, ",multilabel", sizeof(opts));
+ if (flags & MNT_ACLS)
+ strlcat(opts, ",acls", sizeof(opts));
+ if (flags & MNT_NOATIME)
+ strlcat(opts, ",noatime", sizeof(opts));
+ if (flags & MNT_NOCLUSTERR)
+ strlcat(opts, ",noclusterr", sizeof(opts));
+ if (flags & MNT_NOCLUSTERW)
+ strlcat(opts, ",noclusterw", sizeof(opts));
+ if (flags & MNT_NFS4ACLS)
+ strlcat(opts, ",nfs4acls", sizeof(opts));
+#elif __OpenBSD__
+ if (flags & MNT_SYNCHRONOUS)
+ strlcat(opts, ",sync", sizeof(opts));
+ if (flags & MNT_NOEXEC)
+ strlcat(opts, ",noexec", sizeof(opts));
+ if (flags & MNT_NOSUID)
+ strlcat(opts, ",nosuid", sizeof(opts));
+ if (flags & MNT_ASYNC)
+ strlcat(opts, ",async", sizeof(opts));
+ if (flags & MNT_SOFTDEP)
+ strlcat(opts, ",softdep", sizeof(opts));
+ if (flags & MNT_NOATIME)
+ strlcat(opts, ",noatime", sizeof(opts));
+#endif
+ py_tuple = Py_BuildValue("(ssss)",
+ fs[i].f_mntfromname, // device
+ fs[i].f_mntonname, // mount point
+ fs[i].f_fstypename, // fs type
+ opts); // options
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+
+ free(fs);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (fs != NULL)
+ free(fs);
+ return NULL;
+}
+
+
+/*
+ * Return a Python list of named tuples with overall network I/O information
+ */
+static PyObject *
+psutil_net_io_counters(PyObject *self, PyObject *args) {
+ char *buf = NULL, *lim, *next;
+ struct if_msghdr *ifm;
+ int mib[6];
+ size_t len;
+ PyObject *py_retdict = PyDict_New();
+ PyObject *py_ifc_info = NULL;
+ if (py_retdict == NULL)
+ return NULL;
+
+ mib[0] = CTL_NET; // networking subsystem
+ mib[1] = PF_ROUTE; // type of information
+ mib[2] = 0; // protocol (IPPROTO_xxx)
+ mib[3] = 0; // address family
+ mib[4] = NET_RT_IFLIST; // operation
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ lim = buf + len;
+
+ for (next = buf; next < lim; ) {
+ py_ifc_info = NULL;
+ ifm = (struct if_msghdr *)next;
+ next += ifm->ifm_msglen;
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ struct if_msghdr *if2m = (struct if_msghdr *)ifm;
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);
+ char ifc_name[32];
+
+ strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen);
+ ifc_name[sdl->sdl_nlen] = 0;
+ // XXX: ignore usbus interfaces:
+ // http://lists.freebsd.org/pipermail/freebsd-current/
+ // 2011-October/028752.html
+ // 'ifconfig -a' doesn't show them, nor do we.
+ if (strncmp(ifc_name, "usbus", 5) == 0)
+ continue;
+
+ py_ifc_info = Py_BuildValue("(kkkkkkki)",
+ if2m->ifm_data.ifi_obytes,
+ if2m->ifm_data.ifi_ibytes,
+ if2m->ifm_data.ifi_opackets,
+ if2m->ifm_data.ifi_ipackets,
+ if2m->ifm_data.ifi_ierrors,
+ if2m->ifm_data.ifi_oerrors,
+ if2m->ifm_data.ifi_iqdrops,
+ 0); // dropout not supported
+ if (!py_ifc_info)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info))
+ goto error;
+ Py_DECREF(py_ifc_info);
+ }
+ else {
+ continue;
+ }
+ }
+
+ free(buf);
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_ifc_info);
+ Py_DECREF(py_retdict);
+ if (buf != NULL)
+ free(buf);
+ return NULL;
+}
+
+
+#ifdef __FreeBSD__
+/*
+ * Return a Python dict of tuples for disk I/O information
+ */
+static PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+ int i;
+ struct statinfo stats;
+
+ PyObject *py_retdict = PyDict_New();
+ PyObject *py_disk_info = NULL;
+
+ if (py_retdict == NULL)
+ return NULL;
+ if (devstat_checkversion(NULL) < 0) {
+ PyErr_Format(PyExc_RuntimeError, "devstat_checkversion() failed");
+ goto error;
+ }
+
+ stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ if (stats.dinfo == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ bzero(stats.dinfo, sizeof(struct devinfo));
+
+ if (devstat_getdevs(NULL, &stats) == -1) {
+ PyErr_Format(PyExc_RuntimeError, "devstat_getdevs() failed");
+ goto error;
+ }
+
+ for (i = 0; i < stats.dinfo->numdevs; i++) {
+ py_disk_info = NULL;
+ struct devstat current;
+ char disk_name[128];
+ current = stats.dinfo->devices[i];
+ snprintf(disk_name, sizeof(disk_name), "%s%d",
+ current.device_name,
+ current.unit_number);
+
+ py_disk_info = Py_BuildValue(
+ "(KKKKLL)",
+ current.operations[DEVSTAT_READ], // no reads
+ current.operations[DEVSTAT_WRITE], // no writes
+ current.bytes[DEVSTAT_READ], // bytes read
+ current.bytes[DEVSTAT_WRITE], // bytes written
+ (long long) BT2MSEC(current.duration[DEVSTAT_READ]), // r time
+ (long long) BT2MSEC(current.duration[DEVSTAT_WRITE]) // w time
+ ); // finished transactions
+ if (!py_disk_info)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
+ goto error;
+ Py_DECREF(py_disk_info);
+ }
+
+ if (stats.dinfo->mem_ptr)
+ free(stats.dinfo->mem_ptr);
+ free(stats.dinfo);
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_disk_info);
+ Py_DECREF(py_retdict);
+ if (stats.dinfo != NULL)
+ free(stats.dinfo);
+ return NULL;
+}
+#endif
+
+
+/*
+ * Return currently connected users as a list of tuples.
+ */
+static PyObject *
+psutil_users(PyObject *self, PyObject *args) {
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+#if __FreeBSD_version < 900000 || __OpenBSD__
+ struct utmp ut;
+ FILE *fp;
+
+ fp = fopen(_PATH_UTMP, "r");
+ if (fp == NULL) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ while (fread(&ut, sizeof(ut), 1, fp) == 1) {
+ if (*ut.ut_name == '\0')
+ continue;
+ py_tuple = Py_BuildValue(
+ "(sssf)",
+ ut.ut_name, // username
+ ut.ut_line, // tty
+ ut.ut_host, // hostname
+ (float)ut.ut_time); // start time
+ if (!py_tuple) {
+ fclose(fp);
+ goto error;
+ }
+ if (PyList_Append(py_retlist, py_tuple)) {
+ fclose(fp);
+ goto error;
+ }
+ Py_DECREF(py_tuple);
+ }
+
+ fclose(fp);
+#else
+ struct utmpx *utx;
+
+ while ((utx = getutxent()) != NULL) {
+ if (utx->ut_type != USER_PROCESS)
+ continue;
+ py_tuple = Py_BuildValue(
+ "(sssf)",
+ utx->ut_user, // username
+ utx->ut_line, // tty
+ utx->ut_host, // hostname
+ (float)utx->ut_tv.tv_sec // start time
+ );
+
+ if (!py_tuple) {
+ endutxent();
+ goto error;
+ }
+ if (PyList_Append(py_retlist, py_tuple)) {
+ endutxent();
+ goto error;
+ }
+ Py_DECREF(py_tuple);
+ }
+
+ endutxent();
+#endif
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+
+
+#ifdef __FreeBSD__
+/*
+ * System-wide open connections.
+ */
+#define HASHSIZE 1009
+static struct xfile *psutil_xfiles;
+static int psutil_nxfiles;
+
+int
+psutil_populate_xfiles() {
+ size_t len;
+
+ if ((psutil_xfiles = malloc(len = sizeof *psutil_xfiles)) == NULL) {
+ PyErr_NoMemory();
+ return 0;
+ }
+ while (sysctlbyname("kern.file", psutil_xfiles, &len, 0, 0) == -1) {
+ if (errno != ENOMEM) {
+ PyErr_SetFromErrno(0);
+ return 0;
+ }
+ len *= 2;
+ if ((psutil_xfiles = realloc(psutil_xfiles, len)) == NULL) {
+ PyErr_NoMemory();
+ return 0;
+ }
+ }
+ if (len > 0 && psutil_xfiles->xf_size != sizeof *psutil_xfiles) {
+ PyErr_Format(PyExc_RuntimeError, "struct xfile size mismatch");
+ return 0;
+ }
+ psutil_nxfiles = len / sizeof *psutil_xfiles;
+ return 1;
+}
+
+int
+psutil_get_pid_from_sock(int sock_hash) {
+ struct xfile *xf;
+ int hash, n;
+ for (xf = psutil_xfiles, n = 0; n < psutil_nxfiles; ++n, ++xf) {
+ if (xf->xf_data == NULL)
+ continue;
+ hash = (int)((uintptr_t)xf->xf_data % HASHSIZE);
+ if (sock_hash == hash)
+ return xf->xf_pid;
+ }
+ return -1;
+}
+
+
+// Reference:
+// https://gitorious.org/freebsd/freebsd/source/
+// f1d6f4778d2044502209708bc167c05f9aa48615:usr.bin/sockstat/sockstat.c
+int psutil_gather_inet(int proto, PyObject *py_retlist) {
+ struct xinpgen *xig, *exig;
+ struct xinpcb *xip;
+ struct xtcpcb *xtp;
+ struct inpcb *inp;
+ struct xsocket *so;
+ const char *varname = NULL;
+ size_t len, bufsize;
+ void *buf;
+ int hash;
+ int retry;
+ int type;
+
+ PyObject *py_tuple = NULL;
+ PyObject *py_laddr = NULL;
+ PyObject *py_raddr = NULL;
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ varname = "net.inet.tcp.pcblist";
+ type = SOCK_STREAM;
+ break;
+ case IPPROTO_UDP:
+ varname = "net.inet.udp.pcblist";
+ type = SOCK_DGRAM;
+ break;
+ }
+
+ buf = NULL;
+ bufsize = 8192;
+ retry = 5;
+ do {
+ for (;;) {
+ buf = realloc(buf, bufsize);
+ if (buf == NULL)
+ continue; // XXX
+ len = bufsize;
+ if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
+ break;
+ if (errno != ENOMEM) {
+ PyErr_SetFromErrno(0);
+ goto error;
+ }
+ bufsize *= 2;
+ }
+ xig = (struct xinpgen *)buf;
+ exig = (struct xinpgen *)(void *)((char *)buf + len - sizeof *exig);
+ if (xig->xig_len != sizeof *xig || exig->xig_len != sizeof *exig) {
+ PyErr_Format(PyExc_RuntimeError, "struct xinpgen size mismatch");
+ goto error;
+ }
+ } while (xig->xig_gen != exig->xig_gen && retry--);
+
+
+ for (;;) {
+ int lport, rport, pid, status, family;
+
+ xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);
+ if (xig >= exig)
+ break;
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ xtp = (struct xtcpcb *)xig;
+ if (xtp->xt_len != sizeof *xtp) {
+ PyErr_Format(PyExc_RuntimeError,
+ "struct xtcpcb size mismatch");
+ goto error;
+ }
+ inp = &xtp->xt_inp;
+ so = &xtp->xt_socket;
+ status = xtp->xt_tp.t_state;
+ break;
+ case IPPROTO_UDP:
+ xip = (struct xinpcb *)xig;
+ if (xip->xi_len != sizeof *xip) {
+ PyErr_Format(PyExc_RuntimeError,
+ "struct xinpcb size mismatch");
+ goto error;
+ }
+ inp = &xip->xi_inp;
+ so = &xip->xi_socket;
+ status = PSUTIL_CONN_NONE;
+ break;
+ default:
+ PyErr_Format(PyExc_RuntimeError, "invalid proto");
+ goto error;
+ }
+
+ char lip[200], rip[200];
+
+ hash = (int)((uintptr_t)so->xso_so % HASHSIZE);
+ pid = psutil_get_pid_from_sock(hash);
+ if (pid < 0)
+ continue;
+ lport = ntohs(inp->inp_lport);
+ rport = ntohs(inp->inp_fport);
+
+ if (inp->inp_vflag & INP_IPV4) {
+ family = AF_INET;
+ inet_ntop(AF_INET, &inp->inp_laddr.s_addr, lip, sizeof(lip));
+ inet_ntop(AF_INET, &inp->inp_faddr.s_addr, rip, sizeof(rip));
+ }
+ else if (inp->inp_vflag & INP_IPV6) {
+ family = AF_INET6;
+ inet_ntop(AF_INET6, &inp->in6p_laddr.s6_addr, lip, sizeof(lip));
+ inet_ntop(AF_INET6, &inp->in6p_faddr.s6_addr, rip, sizeof(rip));
+ }
+
+ // construct python tuple/list
+ py_laddr = Py_BuildValue("(si)", lip, lport);
+ if (!py_laddr)
+ goto error;
+ if (rport != 0)
+ py_raddr = Py_BuildValue("(si)", rip, rport);
+ else
+ py_raddr = Py_BuildValue("()");
+ if (!py_raddr)
+ goto error;
+ py_tuple = Py_BuildValue("(iiiNNii)", -1, family, type, py_laddr,
+ py_raddr, status, pid);
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+
+ free(buf);
+ return 1;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_laddr);
+ Py_XDECREF(py_raddr);
+ free(buf);
+ return 0;
+}
+
+
+int psutil_gather_unix(int proto, PyObject *py_retlist) {
+ struct xunpgen *xug, *exug;
+ struct xunpcb *xup;
+ const char *varname = NULL;
+ const char *protoname = NULL;
+ size_t len;
+ size_t bufsize;
+ void *buf;
+ int hash;
+ int retry;
+ int pid;
+ struct sockaddr_un *sun;
+ char path[PATH_MAX];
+
+ PyObject *py_tuple = NULL;
+ PyObject *py_laddr = NULL;
+ PyObject *py_raddr = NULL;
+
+ switch (proto) {
+ case SOCK_STREAM:
+ varname = "net.local.stream.pcblist";
+ protoname = "stream";
+ break;
+ case SOCK_DGRAM:
+ varname = "net.local.dgram.pcblist";
+ protoname = "dgram";
+ break;
+ }
+
+ buf = NULL;
+ bufsize = 8192;
+ retry = 5;
+
+ do {
+ for (;;) {
+ buf = realloc(buf, bufsize);
+ if (buf == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ len = bufsize;
+ if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
+ break;
+ if (errno != ENOMEM) {
+ PyErr_SetFromErrno(0);
+ goto error;
+ }
+ bufsize *= 2;
+ }
+ xug = (struct xunpgen *)buf;
+ exug = (struct xunpgen *)(void *)
+ ((char *)buf + len - sizeof *exug);
+ if (xug->xug_len != sizeof *xug || exug->xug_len != sizeof *exug) {
+ PyErr_Format(PyExc_RuntimeError, "struct xinpgen size mismatch");
+ goto error;
+ }
+ } while (xug->xug_gen != exug->xug_gen && retry--);
+
+ for (;;) {
+ xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len);
+ if (xug >= exug)
+ break;
+ xup = (struct xunpcb *)xug;
+ if (xup->xu_len != sizeof *xup)
+ goto error;
+
+ hash = (int)((uintptr_t) xup->xu_socket.xso_so % HASHSIZE);
+ pid = psutil_get_pid_from_sock(hash);
+ if (pid < 0)
+ continue;
+
+ sun = (struct sockaddr_un *)&xup->xu_addr;
+ snprintf(path, sizeof(path), "%.*s",
+ (int)(sun->sun_len - (sizeof(*sun) - sizeof(sun->sun_path))),
+ sun->sun_path);
+
+ py_tuple = Py_BuildValue("(iiisOii)", -1, AF_UNIX, proto, path,
+ Py_None, PSUTIL_CONN_NONE, pid);
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ Py_INCREF(Py_None);
+ }
+
+ free(buf);
+ return 1;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_laddr);
+ Py_XDECREF(py_raddr);
+ free(buf);
+ return 0;
+}
+
+
+/*
+ * Return system-wide open connections.
+ */
+static PyObject*
+psutil_net_connections(PyObject* self, PyObject* args) {
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (psutil_populate_xfiles() != 1)
+ goto error;
+ if (psutil_gather_inet(IPPROTO_TCP, py_retlist) == 0)
+ goto error;
+ if (psutil_gather_inet(IPPROTO_UDP, py_retlist) == 0)
+ goto error;
+ if (psutil_gather_unix(SOCK_STREAM, py_retlist) == 0)
+ goto error;
+ if (psutil_gather_unix(SOCK_DGRAM, py_retlist) == 0)
+ goto error;
+
+ free(psutil_xfiles);
+ return py_retlist;
+
+error:
+ Py_DECREF(py_retlist);
+ free(psutil_xfiles);
+ return NULL;
+}
+
+
+/*
+ * Get process CPU affinity.
+ * Reference: http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c
+ */
+static PyObject*
+psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args) {
+ long pid;
+ int ret;
+ int i;
+ cpuset_t mask;
+ PyObject* py_retlist;
+ PyObject* py_cpu_num;
+
+ if (!PyArg_ParseTuple(args, "i", &pid))
+ return NULL;
+ ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
+ sizeof(mask), &mask);
+ if (ret != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ py_retlist = PyList_New(0);
+ if (py_retlist == NULL)
+ return NULL;
+
+ for (i = 0; i < CPU_SETSIZE; i++) {
+ if (CPU_ISSET(i, &mask)) {
+ py_cpu_num = Py_BuildValue("i", i);
+ if (py_cpu_num == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_cpu_num))
+ goto error;
+ }
+ }
+
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_cpu_num);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+
+
+/*
+ * Set process CPU affinity.
+ * Reference: http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c
+ */
+static PyObject *
+psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
+ long pid;
+ int i;
+ int seq_len;
+ int ret;
+ cpuset_t cpu_set;
+ PyObject *py_cpu_set;
+ PyObject *py_cpu_seq = NULL;
+
+ if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set))
+ return NULL;
+
+ py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer");
+ if (!py_cpu_seq)
+ return NULL;
+ seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);
+
+ // calculate the mask
+ CPU_ZERO(&cpu_set);
+ for (i = 0; i < seq_len; i++) {
+ PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);
+#if PY_MAJOR_VERSION >= 3
+ long value = PyLong_AsLong(item);
+#else
+ long value = PyInt_AsLong(item);
+#endif
+ if (value == -1 && PyErr_Occurred())
+ goto error;
+ CPU_SET(value, &cpu_set);
+ }
+
+ // set affinity
+ ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
+ sizeof(cpu_set), &cpu_set);
+ if (ret != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ Py_DECREF(py_cpu_seq);
+ Py_RETURN_NONE;
+
+error:
+ if (py_cpu_seq != NULL)
+ Py_DECREF(py_cpu_seq);
+ return NULL;
+}
+#endif
+
+/*
+ * define the psutil C module methods and initialize the module.
+ */
+static PyMethodDef
+PsutilMethods[] = {
+ // --- per-process functions
+
+ {"proc_name", psutil_proc_name, METH_VARARGS,
+ "Return process name"},
+ {"proc_connections", psutil_proc_connections, METH_VARARGS,
+ "Return connections opened by process"},
+ {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS,
+ "Return process cmdline as a list of cmdline arguments"},
+ {"proc_ppid", psutil_proc_ppid, METH_VARARGS,
+ "Return process ppid as an integer"},
+ {"proc_uids", psutil_proc_uids, METH_VARARGS,
+ "Return process real effective and saved user ids as a Python tuple"},
+ {"proc_gids", psutil_proc_gids, METH_VARARGS,
+ "Return process real effective and saved group ids as a Python tuple"},
+ {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS,
+ "Return tuple of user/kern time for the given PID"},
+ {"proc_create_time", psutil_proc_create_time, METH_VARARGS,
+ "Return a float indicating the process create time expressed in "
+ "seconds since the epoch"},
+ {"proc_memory_info", psutil_proc_memory_info, METH_VARARGS,
+ "Return extended memory info for a process as a Python tuple."},
+ {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
+ "Return the number of context switches performed by process"},
+ {"proc_threads", psutil_proc_threads, METH_VARARGS,
+ "Return process threads"},
+ {"proc_status", psutil_proc_status, METH_VARARGS,
+ "Return process status as an integer"},
+ {"proc_io_counters", psutil_proc_io_counters, METH_VARARGS,
+ "Return process IO counters"},
+ {"proc_tty_nr", psutil_proc_tty_nr, METH_VARARGS,
+ "Return process tty (terminal) number"},
+ {"proc_cwd", psutil_proc_cwd, METH_VARARGS,
+ "Return process current working directory."},
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 || __OpenBSD__
+ {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS,
+ "Return the number of file descriptors opened by this process"},
+#endif
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 || __OpenBSD__
+ {"proc_open_files", psutil_proc_open_files, METH_VARARGS,
+ "Return files opened by process as a list of (path, fd) tuples"},
+#endif
+
+#ifdef __FreeBSD__
+ {"proc_exe", psutil_proc_exe, METH_VARARGS,
+ "Return process pathname executable"},
+ {"proc_num_threads", psutil_proc_num_threads, METH_VARARGS,
+ "Return number of threads used by process"},
+ {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
+ "Return a list of tuples for every process's memory map"},
+ {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS,
+ "Return process CPU affinity."},
+ {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS,
+ "Set process CPU affinity."},
+ {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS,
+ "Return an XML string to determine the number physical CPUs."},
+#endif
+
+ // --- system-related functions
+
+ {"pids", psutil_pids, METH_VARARGS,
+ "Returns a list of PIDs currently running on the system"},
+ {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS,
+ "Return number of logical CPUs on the system"},
+ {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
+ "Return system virtual memory usage statistics"},
+ {"swap_mem", psutil_swap_mem, METH_VARARGS,
+ "Return swap mem stats"},
+ {"cpu_times", psutil_cpu_times, METH_VARARGS,
+ "Return system cpu times as a tuple (user, system, nice, idle, irc)"},
+ {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
+ "Return system per-cpu times as a list of tuples"},
+ {"boot_time", psutil_boot_time, METH_VARARGS,
+ "Return the system boot time expressed in seconds since the epoch."},
+ {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
+ "Return a list of tuples including device, mount point and "
+ "fs type for all partitions mounted on the system."},
+ {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
+ "Return dict of tuples of networks I/O information."},
+ {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
+ "Return a Python dict of tuples for disk I/O information"},
+ {"users", psutil_users, METH_VARARGS,
+ "Return currently connected users as a list of tuples"},
+#ifdef __FreeBSD__
+ {"net_connections", psutil_net_connections, METH_VARARGS,
+ "Return system-wide open connections."},
+#endif
+ {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_bsd_traverse(PyObject *m, visitproc visit, void *arg) {
+ Py_VISIT(GETSTATE(m)->error);
+ return 0;
+}
+
+static int
+psutil_bsd_clear(PyObject *m) {
+ Py_CLEAR(GETSTATE(m)->error);
+ return 0;
+}
+
+static struct PyModuleDef
+ moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "psutil_bsd",
+ NULL,
+ sizeof(struct module_state),
+ PsutilMethods,
+ NULL,
+ psutil_bsd_traverse,
+ psutil_bsd_clear,
+ NULL
+};
+
+#define INITERROR return NULL
+
+PyMODINIT_FUNC PyInit__psutil_bsd(void)
+
+#else
+#define INITERROR return
+
+void init_psutil_bsd(void)
+#endif
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *module = PyModule_Create(&moduledef);
+#else
+ PyObject *module = Py_InitModule("_psutil_bsd", PsutilMethods);
+#endif
+ PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
+ // process status constants
+
+#ifdef __FreeBSD__
+ PyModule_AddIntConstant(module, "SIDL", SIDL);
+ PyModule_AddIntConstant(module, "SRUN", SRUN);
+ PyModule_AddIntConstant(module, "SSLEEP", SSLEEP);
+ PyModule_AddIntConstant(module, "SSTOP", SSTOP);
+ PyModule_AddIntConstant(module, "SZOMB", SZOMB);
+ PyModule_AddIntConstant(module, "SWAIT", SWAIT);
+ PyModule_AddIntConstant(module, "SLOCK", SLOCK);
+#elif __OpenBSD__
+ PyModule_AddIntConstant(module, "SIDL", SIDL);
+ PyModule_AddIntConstant(module, "SRUN", SRUN);
+ PyModule_AddIntConstant(module, "SSLEEP", SSLEEP);
+ PyModule_AddIntConstant(module, "SSTOP", SSTOP);
+ PyModule_AddIntConstant(module, "SZOMB", SZOMB); // unused
+ PyModule_AddIntConstant(module, "SDEAD", SDEAD);
+ PyModule_AddIntConstant(module, "SONPROC", SONPROC);
+#endif
+
+ // connection status constants
+ 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_RECEIVED", 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);
+ // PSUTIL_CONN_NONE
+ PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", 128);
+
+ if (module == NULL)
+ INITERROR;
+#if PY_MAJOR_VERSION >= 3
+ return module;
+#endif
+}
diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c
index 1e27cdb0..07404206 100644
--- a/psutil/_psutil_posix.c
+++ b/psutil/_psutil_posix.c
@@ -26,7 +26,7 @@
#include <linux/if_packet.h>
#endif // end linux
-#if defined(__FreeBSD__) || defined(__APPLE__)
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
#include <netdb.h>
#include <netinet/in.h>
#include <net/if_dl.h>
@@ -120,7 +120,7 @@ psutil_convert_ipaddr(struct sockaddr *addr, int family) {
data = (const char *)lladdr->sll_addr;
}
#endif
-#if defined(__FreeBSD__) || defined(__APPLE__)
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
else if (addr->sa_family == AF_LINK) {
// Note: prior to Python 3.4 socket module does not expose
// AF_LINK so we'll do.
@@ -250,7 +250,7 @@ error:
* net_if_stats() implementation. This is here because it is common
* to both OSX and FreeBSD and I didn't know where else to put it.
*/
-#if defined(__FreeBSD__) || defined(__APPLE__)
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
#include <sys/sockio.h>
#include <net/if_media.h>
@@ -287,8 +287,8 @@ int psutil_get_nic_speed(int ifm_active) {
case(IFM_1000_SX): // 1000BaseSX - multi-mode fiber
case(IFM_1000_LX): // 1000baseLX - single-mode fiber
case(IFM_1000_CX): // 1000baseCX - 150ohm STP
-#if defined(IFM_1000_TX) && !defined(OPENBSD)
- // FreeBSD 4 and others (but NOT OpenBSD)?
+#if defined(IFM_1000_TX) && !defined(__OpenBSD__)
+ // FreeBSD 4 and others (but NOT OpenBSD) -> #define IFM_1000_T in net/if_media.h
case(IFM_1000_TX):
#endif
#ifdef IFM_1000_FX
@@ -478,7 +478,7 @@ PsutilMethods[] = {
"Set process priority"},
{"net_if_addrs", psutil_net_if_addrs, METH_VARARGS,
"Retrieve NICs information"},
-#if defined(__FreeBSD__) || defined(__APPLE__)
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
{"net_if_stats", psutil_net_if_stats, METH_VARARGS,
"Return NIC stats."},
#endif
@@ -537,7 +537,7 @@ void init_psutil_posix(void)
PyObject *module = Py_InitModule("_psutil_posix", PsutilMethods);
#endif
-#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__sun)
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__sun)
PyModule_AddIntConstant(module, "AF_LINK", AF_LINK);
#endif
diff --git a/psutil/arch/bsd/openbsd.c b/psutil/arch/bsd/openbsd.c
new file mode 100644
index 00000000..5e949241
--- /dev/null
+++ b/psutil/arch/bsd/openbsd.c
@@ -0,0 +1,761 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Platform-specific module methods for OpenBSD.
+ */
+
+#include <Python.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/swap.h> // for swap_mem
+#include <signal.h>
+#include <kvm.h>
+// connection stuff
+#include <netdb.h> // for NI_MAXHOST
+#include <sys/socket.h>
+#include <sys/sched.h> // for CPUSTATES & CP_*
+#define _KERNEL // for DTYPE_*
+#include <sys/file.h>
+#undef _KERNEL
+#include <sys/disk.h> // struct diskstats
+
+
+#include "openbsd.h"
+
+// a signaler for connections without an actual status
+int PSUTIL_CONN_NONE = 128;
+
+#define KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
+#define TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
+
+
+// ============================================================================
+// Utility functions
+// ============================================================================
+
+int
+psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) {
+ // Fills a kinfo_proc struct based on process pid.
+ int ret;
+ int mib[6];
+ size_t size = sizeof(struct kinfo_proc);
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = pid;
+ mib[4] = size;
+ mib[5] = 1;
+
+ ret = sysctl((int*)mib, 6, proc, &size, NULL, 0);
+ if (ret == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ // sysctl stores 0 in the size if we can't find the process information.
+ if (size == 0) {
+ NoSuchProcess();
+ return -1;
+ }
+ return 0;
+}
+
+
+struct kinfo_file *
+kinfo_getfile(long pid, int* cnt) {
+ // Mimic's FreeBSD kinfo_file call, taking a pid and a ptr to an
+ // int as arg and returns an array with cnt struct kinfo_file.
+ int mib[6];
+ size_t len;
+ struct kinfo_file* kf;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_FILE;
+ mib[2] = KERN_FILE_BYPID;
+ mib[3] = (int) pid;
+ mib[4] = sizeof(struct kinfo_file);
+ mib[5] = 0;
+
+ /* get the size of what would be returned */
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ if ((kf = malloc(len)) == NULL) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ mib[5] = (int)(len / sizeof(struct kinfo_file));
+ if (sysctl(mib, 6, kf, &len, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ *cnt = (int)(len / sizeof(struct kinfo_file));
+ return kf;
+}
+
+
+int
+psutil_pid_exists(long pid) {
+ // Return 1 if PID exists in the current process list, else 0, -1
+ // on error.
+ // TODO: this should live in _psutil_posix.c but for some reason if I
+ // move it there I get a "include undefined symbol" error.
+ int ret;
+ if (pid < 0)
+ return 0;
+ ret = kill(pid , 0);
+ if (ret == 0)
+ return 1;
+ else {
+ if (ret == ESRCH)
+ return 0;
+ else if (ret == EPERM)
+ return 1;
+ else {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ }
+}
+
+
+int
+psutil_raise_ad_or_nsp(long pid) {
+ // Set exception to AccessDenied if pid exists else NoSuchProcess.
+ if (psutil_pid_exists(pid) == 0)
+ NoSuchProcess();
+ else
+ AccessDenied();
+}
+
+
+// ============================================================================
+// Process related APIS
+// ============================================================================
+
+int
+psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) {
+ // Returns a list of all BSD processes on the system. This routine
+ // allocates the list and puts it in *procList and a count of the
+ // number of entries in *procCount. You are responsible for freeing
+ // this list (use "free" from System framework).
+ // On success, the function returns 0.
+ // On error, the function returns a BSD errno value.
+ struct kinfo_proc *result;
+ int done;
+ static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC, 0 };
+ // Declaring name as const requires us to cast it when passing it to
+ // sysctl because the prototype doesn't include the const modifier.
+ size_t length;
+ char errbuf[_POSIX2_LINE_MAX];
+ struct kinfo_proc *x;
+ int cnt;
+ kvm_t *kd;
+
+ assert( procList != NULL);
+ assert(*procList == NULL);
+ assert(procCount != NULL);
+
+ kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
+
+ if (kd == NULL) {
+ return errno;
+ }
+
+ result = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &cnt);
+ if (result == NULL) {
+ err(1, NULL);
+ return errno;
+ }
+
+ *procCount = (size_t)cnt;
+
+ size_t mlen = cnt * sizeof(struct kinfo_proc);
+
+ if ((*procList = malloc(mlen)) == NULL) {
+ err(1, NULL);
+ return errno;
+ }
+
+ memcpy(*procList, result, mlen);
+ assert(*procList != NULL);
+ kvm_close(kd);
+
+ return 0;
+}
+
+
+char **
+_psutil_get_argv(long pid) {
+ static char **argv;
+ char **p;
+ int argv_mib[] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV};
+ size_t argv_size = 128;
+ /* Loop and reallocate until we have enough space to fit argv. */
+ for (;; argv_size *= 2) {
+ if ((argv = realloc(argv, argv_size)) == NULL)
+ err(1, NULL);
+ if (sysctl(argv_mib, 4, argv, &argv_size, NULL, 0) == 0)
+ return argv;
+ if (errno == ESRCH) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ if (errno != ENOMEM)
+ err(1, NULL);
+ }
+}
+
+// returns the command line as a python list object
+PyObject *
+psutil_get_cmdline(long pid) {
+ static char **argv;
+ char **p;
+ PyObject *py_arg = NULL;
+ PyObject *py_retlist = Py_BuildValue("[]");
+
+ if (!py_retlist)
+ return NULL;
+ if (pid < 0)
+ return py_retlist;
+
+ if ((argv = _psutil_get_argv(pid)) == NULL)
+ goto error;
+
+ for (p = argv; *p != NULL; p++) {
+ py_arg = Py_BuildValue("s", *p);
+ if (!py_arg)
+ goto error;
+ if (PyList_Append(py_retlist, py_arg))
+ goto error;
+ Py_DECREF(py_arg);
+ }
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_arg);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+
+
+PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+ // OpenBSD reference:
+ // https://github.com/janmojzis/pstree/blob/master/proc_kvm.c
+ // Note: this requires root access, else it will fail trying
+ // to access /dev/kmem.
+ long pid;
+ kvm_t *kd = NULL;
+ int nentries, i;
+ char errbuf[4096];
+ struct kinfo_proc *kp;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ goto error;
+
+ kd = kvm_openfiles(0, 0, 0, O_RDONLY, errbuf);
+ if (! kd) {
+ if (strstr(errbuf, "Permission denied") != NULL)
+ AccessDenied();
+ else
+ PyErr_Format(PyExc_RuntimeError, "kvm_openfiles() failed");
+ goto error;
+ }
+
+ kp = kvm_getprocs(
+ kd, KERN_PROC_PID | KERN_PROC_SHOW_THREADS | KERN_PROC_KTHREAD, pid,
+ sizeof(*kp), &nentries);
+ if (! kp) {
+ if (strstr(errbuf, "Permission denied") != NULL)
+ AccessDenied();
+ else
+ PyErr_Format(PyExc_RuntimeError, "kvm_getprocs() failed");
+ goto error;
+ }
+
+ for (i = 0; i < nentries; i++) {
+ if (kp[i].p_tid < 0)
+ continue;
+ if (kp[i].p_pid == pid) {
+ py_tuple = Py_BuildValue(
+ "Idd",
+ kp[i].p_tid,
+ KPT2DOUBLE(kp[i].p_uutime),
+ KPT2DOUBLE(kp[i].p_ustime));
+ if (py_tuple == NULL)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+ }
+
+ kvm_close(kd);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (kd != NULL)
+ kvm_close(kd);
+ return NULL;
+}
+
+
+PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+ unsigned int total, active, inactive, wired, cached, free;
+ size_t size = sizeof(total);
+ struct uvmexp uvmexp;
+ int mib[] = {CTL_VM, VM_UVMEXP};
+ long pagesize = getpagesize();
+ size = sizeof(uvmexp);
+
+ if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) {
+ warn("failed to get vm.uvmexp");
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ return Py_BuildValue("KKKKKKKK",
+ (unsigned long long) uvmexp.npages * pagesize,
+ (unsigned long long) uvmexp.free * pagesize,
+ (unsigned long long) uvmexp.active * pagesize,
+ (unsigned long long) uvmexp.inactive * pagesize,
+ (unsigned long long) uvmexp.wired * pagesize,
+ (unsigned long long) 0,
+ (unsigned long long) 0,
+ (unsigned long long) 0
+ );
+}
+
+
+PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+ uint64_t swap_total, swap_free;
+ struct swapent *swdev;
+ int nswap, i;
+
+ if ((nswap = swapctl(SWAP_NSWAP, 0, 0)) == 0) {
+ warn("failed to get swap device count");
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ if ((swdev = calloc(nswap, sizeof(*swdev))) == NULL) {
+ warn("failed to allocate memory for swdev structures");
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ if (swapctl(SWAP_STATS, swdev, nswap) == -1) {
+ free(swdev);
+ warn("failed to get swap stats");
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ /* Total things up */
+ swap_total = swap_free = 0;
+ for (i = 0; i < nswap; i++) {
+ if (swdev[i].se_flags & SWF_ENABLE) {
+ swap_free += (swdev[i].se_nblks - swdev[i].se_inuse);
+ swap_total += swdev[i].se_nblks;
+ }
+ }
+ return Py_BuildValue("(LLLII)",
+ swap_total * DEV_BSIZE,
+ (swap_total - swap_free) * DEV_BSIZE,
+ swap_free * DEV_BSIZE,
+ 0 /* XXX swap in */,
+ 0 /* XXX swap out */);
+}
+
+
+PyObject *
+psutil_proc_num_fds(PyObject *self, PyObject *args) {
+ long pid;
+ int cnt;
+
+ struct kinfo_file *freep;
+ struct kinfo_proc kipp;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kipp) == -1)
+ return NULL;
+
+ freep = kinfo_getfile(pid, &cnt);
+ if (freep == NULL) {
+ psutil_raise_ad_or_nsp(pid);
+ return NULL;
+ }
+ free(freep);
+
+ return Py_BuildValue("i", cnt);
+}
+
+
+PyObject *
+psutil_proc_cwd(PyObject *self, PyObject *args) {
+ // Reference:
+ // http://anoncvs.spacehopper.org/openbsd-src/tree/bin/ps/print.c#n179
+ long pid;
+ struct kinfo_proc kp;
+ char path[MAXPATHLEN];
+ size_t pathlen = sizeof path;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (psutil_kinfo_proc(pid, &kp) == -1)
+ return NULL;
+
+ int name[] = { CTL_KERN, KERN_PROC_CWD, pid };
+ if (sysctl(name, 3, path, &pathlen, NULL, 0) != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+ return Py_BuildValue("s", path);
+}
+
+
+// see sys/kern/kern_sysctl.c lines 1100 and
+// usr.bin/fstat/fstat.c print_inet_details()
+static char *
+psutil_convert_ipv4(int family, uint32_t addr[4]) {
+ struct in_addr a;
+ memcpy(&a, addr, sizeof(a));
+ return inet_ntoa(a);
+}
+
+
+static char *
+psutil_inet6_addrstr(struct in6_addr *p)
+{
+ struct sockaddr_in6 sin6;
+ static char hbuf[NI_MAXHOST];
+ const int niflags = NI_NUMERICHOST;
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_addr = *p;
+ if (IN6_IS_ADDR_LINKLOCAL(p) &&
+ *(u_int16_t *)&sin6.sin6_addr.s6_addr[2] != 0) {
+ sin6.sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
+ sin6.sin6_addr.s6_addr[2] = sin6.sin6_addr.s6_addr[3] = 0;
+ }
+
+ if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
+ hbuf, sizeof(hbuf), NULL, 0, niflags))
+ return "invalid";
+
+ return hbuf;
+}
+
+
+PyObject *
+psutil_proc_connections(PyObject *self, PyObject *args) {
+ long pid;
+ int i, cnt;
+
+ struct kinfo_file *freep = NULL;
+ struct kinfo_file *kif;
+ char *tcplist = NULL;
+ struct tcpcb *tcp;
+
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_tuple = NULL;
+ PyObject *py_laddr = NULL;
+ PyObject *py_raddr = NULL;
+ PyObject *py_af_filter = NULL;
+ PyObject *py_type_filter = NULL;
+ PyObject *py_family = NULL;
+ PyObject *_type = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+ if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter))
+ goto error;
+ if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {
+ PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
+ goto error;
+ }
+
+ freep = kinfo_getfile(pid, &cnt);
+ if (freep == NULL) {
+ psutil_raise_ad_or_nsp(pid);
+ goto error;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ int state;
+ int lport;
+ int rport;
+ char path[PATH_MAX];
+ char addrbuf[NI_MAXHOST + 2];
+ int inseq;
+ struct in6_addr laddr6;
+ py_tuple = NULL;
+ py_laddr = NULL;
+ py_raddr = NULL;
+
+ kif = &freep[i];
+ if (kif->f_type == DTYPE_SOCKET) {
+ // apply filters
+ py_family = PyLong_FromLong((long)kif->so_family);
+ inseq = PySequence_Contains(py_af_filter, py_family);
+ Py_DECREF(py_family);
+ if (inseq == 0)
+ continue;
+ _type = PyLong_FromLong((long)kif->so_type);
+ inseq = PySequence_Contains(py_type_filter, _type);
+ Py_DECREF(_type);
+ if (inseq == 0)
+ continue;
+
+ // IPv4 / IPv6 socket
+ if ((kif->so_family == AF_INET) || (kif->so_family == AF_INET6)) {
+ // fill status
+ if (kif->so_type == SOCK_STREAM)
+ state = kif->t_state;
+ else
+ state = PSUTIL_CONN_NONE;
+
+ // ports
+ lport = ntohs(kif->inp_lport);
+ rport = ntohs(kif->inp_fport);
+
+ // local address, IPv4
+ if (kif->so_family == AF_INET) {
+ py_laddr = Py_BuildValue(
+ "(si)",
+ psutil_convert_ipv4(kif->so_family, kif->inp_laddru),
+ lport);
+ if (!py_laddr)
+ goto error;
+ }
+ else {
+ // local address, IPv6
+ memcpy(&laddr6, kif->inp_laddru, sizeof(laddr6));
+ (void *)(uintptr_t)kif->inp_ppcb;
+ snprintf(addrbuf, sizeof(addrbuf), "%s",
+ psutil_inet6_addrstr(&laddr6));
+ py_laddr = Py_BuildValue("(si)", addrbuf, lport);
+ if (!py_laddr)
+ goto error;
+ }
+
+ if (rport != 0) {
+ // remote address, IPv4
+ if (kif->so_family == AF_INET) {
+ py_raddr = Py_BuildValue(
+ "(si)",
+ psutil_convert_ipv4(
+ kif->so_family, kif->inp_faddru),
+ rport);
+ }
+ else {
+ // remote address, IPv6
+ memcpy(&laddr6, kif->inp_faddru, sizeof(laddr6));
+ (void *)(uintptr_t)kif->inp_ppcb;
+ snprintf(addrbuf, sizeof(addrbuf), "%s",
+ psutil_inet6_addrstr(&laddr6));
+ py_raddr = Py_BuildValue("(si)", addrbuf, rport);
+ if (!py_raddr)
+ goto error;
+ }
+ }
+ else {
+ py_raddr = Py_BuildValue("()");
+ }
+
+ if (!py_raddr)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(iiiNNi)",
+ kif->fd_fd,
+ kif->so_family,
+ kif->so_type,
+ py_laddr,
+ py_raddr,
+ state);
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ }
+ // UNIX socket
+ else if (kif->so_family == AF_UNIX) {
+ py_tuple = Py_BuildValue(
+ "(iiisOi)",
+ kif->fd_fd,
+ kif->so_family,
+ kif->so_type,
+ kif->unp_path,
+ Py_None,
+ PSUTIL_CONN_NONE);
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_DECREF(py_tuple);
+ Py_INCREF(Py_None);
+ }
+ }
+ }
+ free(freep);
+ free(tcplist);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_XDECREF(py_laddr);
+ Py_XDECREF(py_raddr);
+ Py_DECREF(py_retlist);
+ if (freep != NULL)
+ free(freep);
+ if (tcplist != NULL)
+ free(tcplist);
+ return NULL;
+}
+
+
+PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+ static int maxcpus;
+ int mib[3];
+ int ncpu;
+ size_t len;
+ size_t size;
+ int i;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_cputime = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+
+ // retrieve the number of cpus
+ mib[0] = CTL_HW;
+ mib[1] = HW_NCPU;
+ len = sizeof(ncpu);
+ if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ uint64_t cpu_time[CPUSTATES];
+
+ for (i = 0; i < ncpu; i++) {
+ // per-cpu info
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_CPTIME2;
+ mib[2] = i;
+ size = sizeof(cpu_time);
+ if (sysctl(mib, 3, &cpu_time, &size, NULL, 0) == -1) {
+ warn("failed to get kern.cptime2");
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ py_cputime = Py_BuildValue(
+ "(ddddd)",
+ (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,
+ (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,
+ (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,
+ (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,
+ (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC);
+ if (!py_cputime)
+ goto error;
+ if (PyList_Append(py_retlist, py_cputime))
+ goto error;
+ Py_DECREF(py_cputime);
+ }
+
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_cputime);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+
+
+PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+ int i, dk_ndrive, mib[3];
+ size_t len;
+ struct diskstats *stats;
+
+ PyObject *py_retdict = PyDict_New();
+ PyObject *py_disk_info = NULL;
+ if (py_retdict == NULL)
+ return NULL;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_DISKSTATS;
+ len = 0;
+ if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) {
+ warn("can't get hw.diskstats size");
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ dk_ndrive = (int)(len / sizeof(struct diskstats));
+
+ stats = malloc(len);
+ if (stats == NULL) {
+ warn("can't malloc");
+ PyErr_NoMemory();
+ goto error;
+ }
+ if (sysctl(mib, 2, stats, &len, NULL, 0) < 0 ) {
+ warn("could not read hw.diskstats");
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < dk_ndrive; i++) {
+ py_disk_info = Py_BuildValue(
+ "(KKKKLL)",
+ stats[i].ds_rxfer,
+ stats[i].ds_wxfer,
+ stats[i].ds_rbytes,
+ stats[i].ds_wbytes,
+ (long long) TV2DOUBLE(stats[i].ds_time) / 2, /* assume half read - half writes.. */
+ (long long) TV2DOUBLE(stats[i].ds_time) / 2);
+ if (!py_disk_info)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, stats[i].ds_name, py_disk_info))
+ goto error;
+ Py_DECREF(py_disk_info);
+ }
+
+ free(stats);
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_disk_info);
+ Py_DECREF(py_retdict);
+ if (stats != NULL)
+ free(stats);
+ return NULL;
+}
diff --git a/psutil/arch/bsd/openbsd.h b/psutil/arch/bsd/openbsd.h
new file mode 100644
index 00000000..e859278e
--- /dev/null
+++ b/psutil/arch/bsd/openbsd.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+typedef struct kinfo_proc kinfo_proc;
+
+int psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc);
+struct kinfo_file * kinfo_getfile(long pid, int* cnt);
+int psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount);
+char **_psutil_get_argv(long pid);
+PyObject * psutil_get_cmdline(long pid);
+int psutil_pid_exists(long pid);
+int psutil_raise_ad_or_nsp(long pid);
+
+//
+PyObject *psutil_proc_threads(PyObject *self, PyObject *args);
+PyObject *psutil_virtual_mem(PyObject *self, PyObject *args);
+PyObject *psutil_swap_mem(PyObject *self, PyObject *args);
+PyObject *psutil_proc_num_fds(PyObject *self, PyObject *args);
+PyObject *psutil_proc_cwd(PyObject *self, PyObject *args);
+PyObject *psutil_proc_connections(PyObject *self, PyObject *args);
+PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);
+PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args);
diff --git a/psutil/arch/bsd/process_info.h b/psutil/arch/bsd/process_info.h
index b70ca23c..a53adc05 100644
--- a/psutil/arch/bsd/process_info.h
+++ b/psutil/arch/bsd/process_info.h
@@ -8,6 +8,9 @@
typedef struct kinfo_proc kinfo_proc;
+#ifdef __OpenBSD__
+struct kinfo_file * kinfo_getfile(long pid, int* cnt);
+#endif
char *psutil_get_cmd_args(long pid, size_t *argsize);
char *psutil_get_cmd_path(long pid, size_t *pathsize);
int psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount);
diff --git a/setup.py b/setup.py
index 3cb2dd63..52bd8998 100644
--- a/setup.py
+++ b/setup.py
@@ -137,6 +137,18 @@ elif sys.platform.startswith("freebsd"):
define_macros=[VERSION_MACRO],
libraries=["devstat"])
extensions = [ext, posix_extension]
+# OpenBSD
+elif sys.platform.startswith("openbsd"):
+ ext = Extension(
+ 'psutil._psutil_bsd',
+ sources=[
+ 'psutil/_psutil_openbsd.c',
+ 'psutil/_psutil_common.c',
+ 'psutil/arch/bsd/openbsd.c'
+ ],
+ define_macros=[VERSION_MACRO],
+ libraries=["kvm"])
+ extensions = [ext, posix_extension]
# Linux
elif sys.platform.startswith("linux"):
def get_ethtool_macro():
@@ -218,6 +230,7 @@ def main():
'Operating System :: Microsoft',
'Operating System :: OS Independent',
'Operating System :: POSIX :: BSD :: FreeBSD',
+ 'Operating System :: POSIX :: BSD :: OpenBSD',
'Operating System :: POSIX :: Linux',
'Operating System :: POSIX :: SunOS/Solaris',
'Operating System :: POSIX',
diff --git a/test/_bsd.py b/test/_freebsd.py
index 7223fb96..1aac6213 100644
--- a/test/_bsd.py
+++ b/test/_freebsd.py
@@ -6,7 +6,7 @@
# TODO: add test for comparing connections with 'sockstat' cmd
-"""BSD specific tests. These are implicitly run by test_psutil.py."""
+"""FreeBSD specific tests. These are implicitly run by test_psutil.py."""
import os
import subprocess
@@ -15,7 +15,7 @@ import time
import psutil
from psutil._compat import PY3
-from test_psutil import BSD
+from test_psutil import FREEBSD
from test_psutil import get_test_subprocess
from test_psutil import MEMORY_TOLERANCE
from test_psutil import reap_children
@@ -55,8 +55,8 @@ def muse(field):
return int(line.split()[1])
-@unittest.skipUnless(BSD, "not a BSD system")
-class BSDSpecificTestCase(unittest.TestCase):
+@unittest.skipUnless(FREEBSD, "not a FreeBSD system")
+class FreeBSDSpecificTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
@@ -248,7 +248,7 @@ class BSDSpecificTestCase(unittest.TestCase):
def main():
test_suite = unittest.TestSuite()
- test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase))
+ test_suite.addTest(unittest.makeSuite(FreeBSDSpecificTestCase))
result = unittest.TextTestRunner(verbosity=2).run(test_suite)
return result.wasSuccessful()
diff --git a/test/test_psutil.py b/test/test_psutil.py
index df00e3d5..9ca4d175 100644
--- a/test/test_psutil.py
+++ b/test/test_psutil.py
@@ -60,6 +60,7 @@ from psutil._compat import callable
from psutil._compat import long
from psutil._compat import PY3
from psutil._compat import unicode
+from psutil._compat import which
if sys.version_info < (2, 7):
import unittest2 as unittest # https://pypi.python.org/pypi/unittest2
@@ -104,7 +105,9 @@ if WINDOWS:
WIN_VISTA = (6, 0, 0)
LINUX = sys.platform.startswith("linux")
OSX = sys.platform.startswith("darwin")
-BSD = sys.platform.startswith("freebsd")
+FREEBSD = sys.platform.startswith("freebsd")
+OPENBSD = sys.platform.startswith("openbsd")
+BSD = FREEBSD or OPENBSD
SUNOS = sys.platform.startswith("sunos")
VALID_PROC_STATUSES = [getattr(psutil, x) for x in dir(psutil)
if x.startswith('STATUS_')]
@@ -214,23 +217,6 @@ def sh(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE):
return stdout.strip()
-def which(program):
- """Same as UNIX which command. Return None on command not found."""
- def is_exe(fpath):
- return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
-
- fpath, fname = os.path.split(program)
- if fpath:
- if is_exe(program):
- return program
- else:
- for path in os.environ["PATH"].split(os.pathsep):
- exe_file = os.path.join(path, program)
- if is_exe(exe_file):
- return exe_file
- return None
-
-
if POSIX:
def get_kernel_version():
"""Return a tuple such as (2, 6, 36)."""
@@ -1585,7 +1571,13 @@ class TestProcess(unittest.TestCase):
# thread number, since we always have with 1 thread per process,
# but this does not apply across all platforms (OSX, Windows)
p = psutil.Process()
- step1 = p.num_threads()
+ if OPENBSD:
+ try:
+ step1 = p.num_threads()
+ except psutil.AccessDenied:
+ raise unittest.SkipTest("on OpenBSD this requires root access")
+ else:
+ step1 = p.num_threads()
thread = ThreadTask()
thread.start()
@@ -1605,7 +1597,13 @@ class TestProcess(unittest.TestCase):
def test_threads(self):
p = psutil.Process()
- step1 = p.threads()
+ if OPENBSD:
+ try:
+ step1 = p.threads()
+ except psutil.AccessDenied:
+ raise unittest.SkipTest("on OpenBSD this requires root access")
+ else:
+ step1 = p.threads()
thread = ThreadTask()
thread.start()
@@ -1629,6 +1627,12 @@ class TestProcess(unittest.TestCase):
def test_threads_2(self):
p = psutil.Process()
+ if OPENBSD:
+ try:
+ p.threads()
+ except psutil.AccessDenied:
+ raise unittest.SkipTest(
+ "on OpenBSD this requires root access")
self.assertAlmostEqual(p.cpu_times().user,
p.threads()[0].user_time, delta=0.1)
self.assertAlmostEqual(p.cpu_times().system,
@@ -1657,6 +1661,7 @@ class TestProcess(unittest.TestCase):
# def test_memory_info_ex(self):
# # tested later in fetch all test suite
+ @unittest.skipIf(OPENBSD, "not available on OpenBSD")
def test_memory_maps(self):
p = psutil.Process()
maps = p.memory_maps()
@@ -1847,7 +1852,7 @@ class TestProcess(unittest.TestCase):
p = psutil.Process(sproc.pid)
call_until(p.cwd, "ret == os.path.dirname(os.getcwd())")
- @unittest.skipUnless(WINDOWS or LINUX or BSD,
+ @unittest.skipUnless(WINDOWS or LINUX or FREEBSD,
'not available on this platform')
@unittest.skipIf(LINUX and TRAVIS, "unknown failure on travis")
def test_cpu_affinity(self):
@@ -1946,8 +1951,8 @@ class TestProcess(unittest.TestCase):
for c in psutil.net_connections(kind='all'):
if c.pid == pid:
sys_cons.append(pconn(*c[:-1]))
- if BSD:
- # on BSD all fds are set to -1
+ if FREEBSD:
+ # on FreeBSD all fds are set to -1
proc_cons = [pconn(*[-1] + list(x[1:])) for x in proc_cons]
self.assertEqual(sorted(proc_cons), sorted(sys_cons))
@@ -2123,6 +2128,7 @@ class TestProcess(unittest.TestCase):
self.assertEqual(p.num_fds(), start)
@skip_on_not_implemented(only_if=LINUX)
+ @unittest.skipIf(OPENBSD, "not reliable on OpenBSD")
def test_num_ctx_switches(self):
p = psutil.Process()
before = sum(p.num_ctx_switches())
@@ -2280,6 +2286,11 @@ class TestProcess(unittest.TestCase):
name)
except psutil.NoSuchProcess:
pass
+ except psutil.AccessDenied:
+ if OPENBSD and name in ('threads', 'num_threads'):
+ pass
+ else:
+ raise
except NotImplementedError:
pass
else:
@@ -2377,7 +2388,7 @@ class TestProcess(unittest.TestCase):
def test_pid_0(self):
# Process(0) is supposed to work on all platforms except Linux
- if 0 not in psutil.pids():
+ if 0 not in psutil.pids() and not OPENBSD:
self.assertRaises(psutil.NoSuchProcess, psutil.Process, 0)
return
@@ -2418,8 +2429,11 @@ class TestProcess(unittest.TestCase):
except psutil.AccessDenied:
pass
- self.assertIn(0, psutil.pids())
- self.assertTrue(psutil.pid_exists(0))
+ p.as_dict()
+
+ if not OPENBSD:
+ self.assertIn(0, psutil.pids())
+ self.assertTrue(psutil.pid_exists(0))
def test_Popen(self):
# Popen class test
@@ -2505,7 +2519,7 @@ class TestFetchAllProcesses(unittest.TestCase):
if ret not in (0, 0.0, [], None, ''):
assert ret, ret
meth = getattr(self, name)
- meth(ret)
+ meth(ret, p)
except Exception as err:
s = '\n' + '=' * 70 + '\n'
s += "FAIL: test_%s (proc=%s" % (name, p)
@@ -2515,6 +2529,7 @@ class TestFetchAllProcesses(unittest.TestCase):
s += '-' * 70
s += "\n%s" % traceback.format_exc()
s = "\n".join((" " * 4) + i for i in s.splitlines())
+ s += '\n'
failures.append(s)
break
@@ -2525,10 +2540,10 @@ class TestFetchAllProcesses(unittest.TestCase):
# special cases.
self.assertTrue(valid_procs > 0)
- def cmdline(self, ret):
+ def cmdline(self, ret, proc):
pass
- def exe(self, ret):
+ def exe(self, ret, proc):
if not ret:
self.assertEqual(ret, '')
else:
@@ -2541,49 +2556,55 @@ class TestFetchAllProcesses(unittest.TestCase):
# XXX may fail on OSX
self.assertTrue(os.access(ret, os.X_OK))
- def ppid(self, ret):
+ def ppid(self, ret, proc):
self.assertTrue(ret >= 0)
- def name(self, ret):
+ def name(self, ret, proc):
self.assertIsInstance(ret, (str, unicode))
self.assertTrue(ret)
- def create_time(self, ret):
- self.assertTrue(ret > 0)
+ def create_time(self, ret, proc):
+ try:
+ self.assertGreaterEqual(ret, 0)
+ except AssertionError:
+ if OPENBSD and proc.status == psutil.STATUS_ZOMBIE:
+ pass
+ else:
+ raise
# this can't be taken for granted on all platforms
# self.assertGreaterEqual(ret, psutil.boot_time())
# make sure returned value can be pretty printed
# with strftime
time.strftime("%Y %m %d %H:%M:%S", time.localtime(ret))
- def uids(self, ret):
+ def uids(self, ret, proc):
for uid in ret:
self.assertTrue(uid >= 0)
self.assertIn(uid, self._uids)
- def gids(self, ret):
+ def gids(self, ret, proc):
# note: testing all gids as above seems not to be reliable for
# gid == 30 (nodoby); not sure why.
for gid in ret:
self.assertTrue(gid >= 0)
# self.assertIn(uid, self.gids
- def username(self, ret):
+ def username(self, ret, proc):
self.assertTrue(ret)
if POSIX:
self.assertIn(ret, self._usernames)
- def status(self, ret):
+ def status(self, ret, proc):
self.assertTrue(ret != "")
self.assertTrue(ret != '?')
self.assertIn(ret, VALID_PROC_STATUSES)
- def io_counters(self, ret):
+ def io_counters(self, ret, proc):
for field in ret:
if field != -1:
self.assertTrue(field >= 0)
- def ionice(self, ret):
+ def ionice(self, ret, proc):
if LINUX:
self.assertTrue(ret.ioclass >= 0)
self.assertTrue(ret.value >= 0)
@@ -2591,24 +2612,24 @@ class TestFetchAllProcesses(unittest.TestCase):
self.assertTrue(ret >= 0)
self.assertIn(ret, (0, 1, 2))
- def num_threads(self, ret):
+ def num_threads(self, ret, proc):
self.assertTrue(ret >= 1)
- def threads(self, ret):
+ def threads(self, ret, proc):
for t in ret:
self.assertTrue(t.id >= 0)
self.assertTrue(t.user_time >= 0)
self.assertTrue(t.system_time >= 0)
- def cpu_times(self, ret):
+ def cpu_times(self, ret, proc):
self.assertTrue(ret.user >= 0)
self.assertTrue(ret.system >= 0)
- def memory_info(self, ret):
+ def memory_info(self, ret, proc):
self.assertTrue(ret.rss >= 0)
self.assertTrue(ret.vms >= 0)
- def memory_info_ex(self, ret):
+ def memory_info_ex(self, ret, proc):
for name in ret._fields:
self.assertTrue(getattr(ret, name) >= 0)
if POSIX and ret.vms != 0:
@@ -2623,7 +2644,7 @@ class TestFetchAllProcesses(unittest.TestCase):
assert ret.peak_nonpaged_pool >= ret.nonpaged_pool, ret
assert ret.peak_pagefile >= ret.pagefile, ret
- def open_files(self, ret):
+ def open_files(self, ret, proc):
for f in ret:
if WINDOWS:
assert f.fd == -1, f
@@ -2632,15 +2653,15 @@ class TestFetchAllProcesses(unittest.TestCase):
assert os.path.isabs(f.path), f
assert os.path.isfile(f.path), f
- def num_fds(self, ret):
+ def num_fds(self, ret, proc):
self.assertTrue(ret >= 0)
- def connections(self, ret):
+ def connections(self, ret, proc):
self.assertEqual(len(ret), len(set(ret)))
for conn in ret:
check_connection_ntuple(conn)
- def cwd(self, ret):
+ def cwd(self, ret, proc):
if ret is not None: # BSD may return None
assert os.path.isabs(ret), ret
try:
@@ -2652,21 +2673,21 @@ class TestFetchAllProcesses(unittest.TestCase):
else:
self.assertTrue(stat.S_ISDIR(st.st_mode))
- def memory_percent(self, ret):
+ def memory_percent(self, ret, proc):
assert 0 <= ret <= 100, ret
- def is_running(self, ret):
+ def is_running(self, ret, proc):
self.assertTrue(ret)
- def cpu_affinity(self, ret):
+ def cpu_affinity(self, ret, proc):
assert ret != [], ret
- def terminal(self, ret):
+ def terminal(self, ret, proc):
if ret is not None:
assert os.path.isabs(ret), ret
assert os.path.exists(ret), ret
- def memory_maps(self, ret):
+ def memory_maps(self, ret, proc):
for nt in ret:
for fname in nt._fields:
value = getattr(nt, fname)
@@ -2682,13 +2703,13 @@ class TestFetchAllProcesses(unittest.TestCase):
self.assertIsInstance(value, (int, long))
assert value >= 0, value
- def num_handles(self, ret):
+ def num_handles(self, ret, proc):
if WINDOWS:
self.assertGreaterEqual(ret, 0)
else:
self.assertGreaterEqual(ret, 0)
- def nice(self, ret):
+ def nice(self, ret, proc):
if POSIX:
assert -20 <= ret <= 20, ret
else:
@@ -2696,11 +2717,11 @@ class TestFetchAllProcesses(unittest.TestCase):
if x.endswith('_PRIORITY_CLASS')]
self.assertIn(ret, priorities)
- def num_ctx_switches(self, ret):
+ def num_ctx_switches(self, ret, proc):
self.assertTrue(ret.voluntary >= 0)
self.assertTrue(ret.involuntary >= 0)
- def rlimit(self, ret):
+ def rlimit(self, ret, proc):
self.assertEqual(len(ret), 2)
self.assertGreaterEqual(ret[0], -1)
self.assertGreaterEqual(ret[1], -1)
@@ -3092,6 +3113,7 @@ class TestExampleScripts(unittest.TestCase):
def test_ifconfig(self):
self.assert_stdout('ifconfig.py')
+ @unittest.skipIf(OPENBSD, "OpenBSD does not support memory maps")
def test_pmap(self):
self.assert_stdout('pmap.py', args=str(os.getpid()))
@@ -3204,8 +3226,8 @@ def main():
tests.append(TestDualProcessImplementation)
elif OSX:
from _osx import OSXSpecificTestCase as stc
- elif BSD:
- from _bsd import BSDSpecificTestCase as stc
+ elif FREEBSD:
+ from _freebsd import FreeBSDSpecificTestCase as stc
elif SUNOS:
from _sunos import SunOSSpecificTestCase as stc
if stc is not None: