Files
haikuports/dev-python/psutil/patches/psutil-5.6.7.patchset
François Revol 8415ad930d Add recipe for psutil
And partial patch, but not enough to run glances yet.
2020-02-13 01:19:54 +01:00

1411 lines
39 KiB
Plaintext

From cad8406514db2b2241d0e522ede4d19267c8e441 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= <revol@free.fr>
Date: Wed, 12 Feb 2020 04:16:40 +0100
Subject: [PATCH] WIP: preliminary Haiku port
---
psutil/__init__.py | 5 +
psutil/_common.py | 5 +-
psutil/_pshaiku.py | 433 ++++++++++++++++++++
psutil/_psutil_haiku.cpp | 841 +++++++++++++++++++++++++++++++++++++++
psutil/_psutil_posix.c | 5 +-
setup.py | 11 +
6 files changed, 1297 insertions(+), 3 deletions(-)
create mode 100644 psutil/_pshaiku.py
create mode 100644 psutil/_psutil_haiku.cpp
diff --git a/psutil/__init__.py b/psutil/__init__.py
index b267239..0ba8fc7 100644
--- a/psutil/__init__.py
+++ b/psutil/__init__.py
@@ -16,6 +16,7 @@ sensors) in Python. Supported platforms:
- NetBSD
- Sun Solaris
- AIX
+ - Haiku
Works with Python versions from 2.6 to 3.4+.
"""
@@ -79,6 +80,7 @@ from ._common import NIC_DUPLEX_UNKNOWN
from ._common import AIX
from ._common import BSD
from ._common import FREEBSD # NOQA
+from ._common import HAIKU
from ._common import LINUX
from ._common import MACOS
from ._common import NETBSD # NOQA
@@ -174,6 +176,9 @@ elif AIX:
# via sys.modules.
PROCFS_PATH = "/proc"
+elif HAIKU:
+ from . import _pshaiku as _psplatform
+
else: # pragma: no cover
raise NotImplementedError('platform %s is not supported' % sys.platform)
diff --git a/psutil/_common.py b/psutil/_common.py
index 126d9d6..88ac58f 100644
--- a/psutil/_common.py
+++ b/psutil/_common.py
@@ -42,8 +42,8 @@ PY3 = sys.version_info[0] == 3
__all__ = [
# constants
- 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX',
- 'SUNOS', 'WINDOWS',
+ 'FREEBSD', 'BSD', 'HAIKU', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX',
+ 'POSIX', 'SUNOS', 'WINDOWS',
'ENCODING', 'ENCODING_ERRS', 'AF_INET6',
# connection constants
'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED',
@@ -84,6 +84,7 @@ NETBSD = sys.platform.startswith("netbsd")
BSD = FREEBSD or OPENBSD or NETBSD
SUNOS = sys.platform.startswith(("sunos", "solaris"))
AIX = sys.platform.startswith("aix")
+HAIKU = sys.platform.startswith("haiku")
# ===================================================================
diff --git a/psutil/_pshaiku.py b/psutil/_pshaiku.py
new file mode 100644
index 0000000..89115f7
--- /dev/null
+++ b/psutil/_pshaiku.py
@@ -0,0 +1,433 @@
+# Copyright (c) 2009, Giampaolo Rodola'
+# Copyright (c) 2017, Arnon Yaari
+# Copyright (c) 2020, François Revol
+# All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Haiku platform implementation."""
+
+import functools
+# import glob
+import os
+# import re
+# import subprocess
+# import sys
+from collections import namedtuple
+
+from . import _common
+from . import _psposix
+from . import _psutil_haiku as cext
+from . import _psutil_posix as cext_posix
+from ._common import AccessDenied
+# from ._common import conn_to_ntuple
+from ._common import memoize_when_activated
+from ._common import NoSuchProcess
+from ._common import usage_percent
+from ._common import ZombieProcess
+from ._compat import FileNotFoundError
+from ._compat import PermissionError
+from ._compat import ProcessLookupError
+# from ._compat import PY3
+
+
+__extra__all__ = []
+
+
+# =====================================================================
+# --- globals
+# =====================================================================
+
+
+HAS_NET_IO_COUNTERS = hasattr(cext, "net_io_counters")
+HAS_PROC_IO_COUNTERS = hasattr(cext, "proc_io_counters")
+
+PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
+AF_LINK = cext_posix.AF_LINK
+
+PROC_STATUSES = {
+ cext.B_THREAD_RUNNING: _common.STATUS_RUNNING,
+ cext.B_THREAD_READY: _common.STATUS_IDLE,
+ cext.B_THREAD_RECEIVING: _common.STATUS_WAITING,
+ cext.B_THREAD_ASLEEP: _common.STATUS_SLEEPING,
+ cext.B_THREAD_SUSPENDED: _common.STATUS_STOPPED,
+ cext.B_THREAD_WAITING: _common.STATUS_WAITING,
+}
+
+team_info_map = dict(
+ thread_count=0,
+ image_count=1,
+ area_count=2,
+ uid=3,
+ gid=4,
+ name=5)
+
+team_usage_info_map = dict(
+ user_time=0,
+ kernel_time=1)
+
+thread_info_map = dict(
+ id=0,
+ user_time=1,
+ kernel_time=2,
+ state=3)
+
+
+# =====================================================================
+# --- named tuples
+# =====================================================================
+
+
+# psutil.Process.memory_info()
+pmem = namedtuple('pmem', ['rss', 'vms'])
+# psutil.Process.memory_full_info()
+pfullmem = pmem
+# psutil.Process.cpu_times()
+scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
+# psutil.virtual_memory()
+svmem = namedtuple('svmem', ['total', 'used', 'percent', 'cached', 'buffers',
+ 'ignored', 'needed', 'available'])
+
+
+# =====================================================================
+# --- memory
+# =====================================================================
+
+
+def virtual_memory():
+ total, inuse, cached, buffers, ignored, needed, avail = cext.virtual_mem()
+ percent = usage_percent((total - avail), total, round_=1)
+ return svmem(total, inuse, percent, cached, buffers, ignored, needed,
+ avail)
+
+
+def swap_memory():
+ """Swap system memory as a (total, used, free, sin, sout) tuple."""
+ total, free = cext.swap_mem()
+ sin = 0
+ sout = 0
+ used = total - free
+ percent = usage_percent(used, total, round_=1)
+ return _common.sswap(total, used, free, percent, sin, sout)
+
+
+# =====================================================================
+# --- CPU
+# =====================================================================
+
+
+def cpu_times():
+ """Return system-wide CPU times as a named tuple"""
+ ret = cext.per_cpu_times()
+ return scputimes(*[sum(x) for x in zip(*ret)])
+
+
+def per_cpu_times():
+ """Return system per-CPU times as a list of named tuples"""
+ ret = cext.per_cpu_times()
+ return [scputimes(*x) for x in ret]
+
+
+def cpu_count_logical():
+ """Return the number of logical CPUs in the system."""
+ try:
+ return os.sysconf("SC_NPROCESSORS_ONLN")
+ except ValueError:
+ # mimic os.cpu_count() behavior
+ return None
+
+
+def cpu_count_physical():
+ # TODO:
+ return None
+
+
+def cpu_stats():
+ """Return various CPU stats as a named tuple."""
+ ctx_switches, interrupts, soft_interrupts, syscalls = cext.cpu_stats()
+ return _common.scpustats(
+ ctx_switches, interrupts, soft_interrupts, syscalls)
+
+
+def cpu_freq():
+ """Return CPU frequency.
+ """
+ curr, min_, max_ = cext.cpu_freq()
+ return [_common.scpufreq(curr, min_, max_)]
+
+
+# =====================================================================
+# --- disks
+# =====================================================================
+
+
+disk_io_counters = cext.disk_io_counters
+disk_usage = _psposix.disk_usage
+
+
+def disk_partitions(all=False):
+ """Return system disk partitions."""
+ # TODO - the filtering logic should be better checked so that
+ # it tries to reflect 'df' as much as possible
+ retlist = []
+ partitions = cext.disk_partitions()
+ for partition in partitions:
+ device, mountpoint, fstype, opts = partition
+ if device == 'none':
+ device = ''
+ if not all:
+ # Differently from, say, Linux, we don't have a list of
+ # common fs types so the best we can do, AFAIK, is to
+ # filter by filesystem having a total size > 0.
+ if not disk_usage(mountpoint).total:
+ continue
+ ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
+ retlist.append(ntuple)
+ return retlist
+
+
+# =====================================================================
+# --- network
+# =====================================================================
+
+
+net_if_addrs = cext_posix.net_if_addrs
+
+if HAS_NET_IO_COUNTERS:
+ net_io_counters = cext.net_io_counters
+
+
+def net_connections(kind, _pid=-1):
+ """Return socket connections. If pid == -1 return system-wide
+ connections (as opposed to connections opened by one process only).
+ """
+ # TODO
+ return None
+
+
+def net_if_stats():
+ """Get NIC stats (isup, duplex, speed, mtu)."""
+ # TODO
+ return None
+
+
+# =====================================================================
+# --- other system functions
+# =====================================================================
+
+
+def boot_time():
+ """The system boot time expressed in seconds since the epoch."""
+ return cext.boot_time()
+
+
+def users():
+ """Return currently connected users as a list of namedtuples."""
+ retlist = []
+ rawlist = cext.users()
+ localhost = (':0.0', ':0')
+ for item in rawlist:
+ user, tty, hostname, tstamp, user_process, pid = item
+ # note: the underlying C function includes entries about
+ # system boot, run level and others. We might want
+ # to use them in the future.
+ if not user_process:
+ continue
+ if hostname in localhost:
+ hostname = 'localhost'
+ nt = _common.suser(user, tty, hostname, tstamp, pid)
+ retlist.append(nt)
+ return retlist
+
+
+# =====================================================================
+# --- processes
+# =====================================================================
+
+
+def pids():
+ ls = cext.pids()
+ return ls
+
+
+pid_exists = _psposix.pid_exists
+
+
+def wrap_exceptions(fun):
+ """Call callable into a try/except clause and translate ENOENT,
+ EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
+ """
+ @functools.wraps(fun)
+ def wrapper(self, *args, **kwargs):
+ try:
+ return fun(self, *args, **kwargs)
+ except (FileNotFoundError, ProcessLookupError):
+ # ENOENT (no such file or directory) gets raised on open().
+ # ESRCH (no such process) can get raised on read() if
+ # process is gone in meantime.
+ if not pid_exists(self.pid):
+ raise NoSuchProcess(self.pid, self._name)
+ else:
+ raise ZombieProcess(self.pid, self._name, self._ppid)
+ except PermissionError:
+ raise AccessDenied(self.pid, self._name)
+ return wrapper
+
+
+class Process(object):
+ """Wrapper class around underlying C implementation."""
+
+ __slots__ = ["pid", "_name", "_ppid", "_cache"]
+
+ def __init__(self, pid):
+ self.pid = pid
+ self._name = None
+ self._ppid = None
+
+ def oneshot_enter(self):
+ self._proc_team_info.cache_activate(self)
+ self._proc_team_usage_info.cache_activate(self)
+
+ def oneshot_exit(self):
+ self._proc_team_info.cache_deactivate(self)
+ self._proc_team_usage_info.cache_deactivate(self)
+
+ @wrap_exceptions
+ @memoize_when_activated
+ def _proc_team_info(self):
+ ret = cext.proc_team_info_oneshot(self.pid)
+ print("%d %d\n" % (len(ret), len(team_info_map)))
+ assert len(ret) == len(team_info_map)
+ return ret
+
+ @wrap_exceptions
+ @memoize_when_activated
+ def _proc_team_usage_info(self):
+ ret = cext.proc_team_usage_info_oneshot(self.pid)
+ print("%d %d\n" % (len(ret), len(team_usage_info_map)))
+ assert len(ret) == len(team_usage_info_map)
+ return ret
+
+ @wrap_exceptions
+ def name(self):
+ return cext.proc_name(self.pid).rstrip("\x00")
+
+ @wrap_exceptions
+ def exe(self):
+ return cext.proc_exe(self.pid)
+
+ @wrap_exceptions
+ def cmdline(self):
+ return cext.proc_cmdline(self.pid)
+
+ @wrap_exceptions
+ def environ(self):
+ return cext.proc_environ(self.pid)
+
+ @wrap_exceptions
+ def create_time(self):
+ return None
+
+ @wrap_exceptions
+ def num_threads(self):
+ return self._proc_team_info()[team_info_map['thread_count']]
+
+ @wrap_exceptions
+ def threads(self):
+ rawlist = cext.proc_threads(self.pid)
+ retlist = []
+ for thread_id, utime, stime, state in rawlist:
+ ntuple = _common.pthread(thread_id, utime, stime)
+ retlist.append(ntuple)
+ return retlist
+
+ @wrap_exceptions
+ def connections(self, kind='inet'):
+ ret = net_connections(kind, _pid=self.pid)
+ # The underlying C implementation retrieves all OS connections
+ # and filters them by PID. At this point we can't tell whether
+ # an empty list means there were no connections for process or
+ # process is no longer active so we force NSP in case the PID
+ # is no longer there.
+ if not ret:
+ # will raise NSP if process is gone
+ os.stat('%s/%s' % (self._procfs_path, self.pid))
+ return ret
+
+ @wrap_exceptions
+ def nice_get(self):
+ return cext_posix.getpriority(self.pid)
+
+ @wrap_exceptions
+ def nice_set(self, value):
+ return cext_posix.setpriority(self.pid, value)
+
+ @wrap_exceptions
+ def ppid(self):
+ return None
+
+ @wrap_exceptions
+ def uids(self):
+ uid = self._proc_team_info()[team_info_map['uid']]
+ return _common.puids(uid, uid, uid)
+
+ @wrap_exceptions
+ def gids(self):
+ gid = self._proc_team_info()[team_info_map['gid']]
+ return _common.puids(gid, gid, gid)
+
+ @wrap_exceptions
+ def cpu_times(self):
+ _user, _kern = self._proc_team_usage_info()
+ return _common.pcputimes(_user, _kern, _user, _kern)
+
+ @wrap_exceptions
+ def terminal(self):
+ # TODO
+ return None
+
+ @wrap_exceptions
+ def cwd(self):
+ return None
+
+ @wrap_exceptions
+ def memory_info(self):
+ # TODO:
+ rss = 0
+ vms = 0
+ return pmem(rss, vms)
+
+ memory_full_info = memory_info
+
+ @wrap_exceptions
+ def status(self):
+ threads = cext.proc_threads(self.pid)
+ code = threads[0][thread_info_map['state']]
+ # XXX is '?' legit? (we're not supposed to return it anyway)
+ return PROC_STATUSES.get(code, '?')
+
+ def open_files(self):
+ # TODO:
+ return []
+
+ @wrap_exceptions
+ def num_fds(self):
+ # TODO:
+ return None
+
+ @wrap_exceptions
+ def num_ctx_switches(self):
+ return _common.pctxsw(
+ *cext.proc_num_ctx_switches(self.pid))
+
+ @wrap_exceptions
+ def wait(self, timeout=None):
+ return _psposix.wait_pid(self.pid, timeout, self._name)
+
+ @wrap_exceptions
+ def io_counters(self):
+ return _common.pio(
+ 0,
+ 0,
+ -1,
+ -1)
diff --git a/psutil/_psutil_haiku.cpp b/psutil/_psutil_haiku.cpp
new file mode 100644
index 0000000..f9b3a44
--- /dev/null
+++ b/psutil/_psutil_haiku.cpp
@@ -0,0 +1,841 @@
+/*
+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * macOS platform-specific module methods.
+ */
+
+#include <Python.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <pwd.h>
+
+#include <fs_info.h>
+#include <image.h>
+#include <OS.h>
+#include <String.h>
+
+extern "C" {
+
+#include "_psutil_common.h"
+#include "_psutil_posix.h"
+//#include "arch/haiku/process_info.h"
+
+}
+
+static PyObject *ZombieProcessError;
+
+
+/*
+ * Return a Python list of all the PIDs running on the system.
+ */
+static PyObject *
+psutil_pids(PyObject *self, PyObject *args) {
+ int32 cookie = 0;
+ team_info info;
+ PyObject *py_pid = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ while (get_next_team_info(&cookie, &info) == B_OK) {
+ /* quite wasteful: we have all team infos already */
+ py_pid = Py_BuildValue("i", info.team);
+ if (! py_pid)
+ goto error;
+ if (PyList_Append(py_retlist, py_pid))
+ goto error;
+ Py_CLEAR(py_pid);
+ }
+
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_pid);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+
+
+/*
+ * Return multiple process info as a Python tuple in one shot by
+ * using get_team_info() and filling up a team_info struct.
+ */
+static PyObject *
+psutil_proc_team_info_oneshot(PyObject *self, PyObject *args) {
+ pid_t pid;
+ team_info info;
+ PyObject *py_name;
+ PyObject *py_retlist;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (get_team_info(pid, &info) != B_OK)
+ return NULL;
+
+ py_name = PyUnicode_DecodeFSDefault(info.args);
+ if (! py_name) {
+ // Likely a decoding error. We don't want to fail the whole
+ // operation. The python module may retry with proc_name().
+ PyErr_Clear();
+ py_name = Py_None;
+ }
+
+ py_retlist = Py_BuildValue(
+ "lllllO",
+ (long)info.thread_count, // (long) thread_count
+ (long)info.image_count, // (long) image_count
+ (long)info.area_count, // (long) area_count
+ (long)info.uid, // (long) uid
+ (long)info.gid, // (long) gid
+ py_name // (pystr) name
+ );
+
+ if (py_retlist != NULL) {
+ // XXX shall we decref() also in case of Py_BuildValue() error?
+ Py_DECREF(py_name);
+ }
+ return py_retlist;
+}
+
+
+/*
+ * Return multiple process info as a Python tuple in one shot by
+ * using get_team_usage_info() and filling up a team_usage_info struct.
+ */
+static PyObject *
+psutil_proc_team_usage_info_oneshot(PyObject *self, PyObject *args) {
+ pid_t pid;
+ team_usage_info info;
+ PyObject *py_retlist;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (get_team_usage_info(pid, B_TEAM_USAGE_SELF, &info) != B_OK)
+ return NULL;
+
+ py_retlist = Py_BuildValue(
+ "KK",
+ (unsigned long long)info.user_time,
+ (unsigned long long)info.kernel_time
+ );
+
+ return py_retlist;
+}
+
+
+/*
+ * Return process name from kinfo_proc as a Python string.
+ */
+static PyObject *
+psutil_proc_name(PyObject *self, PyObject *args) {
+ pid_t pid;
+ team_info info;
+ PyObject *py_name;
+ PyObject *py_retlist;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ if (get_team_info(pid, &info) != B_OK)
+ return NULL;
+
+ return PyUnicode_DecodeFSDefault(info.args);
+}
+
+
+/*
+ * Return process current working directory.
+ * Raises NSP in case of zombie process.
+ */
+static PyObject *
+psutil_proc_cwd(PyObject *self, PyObject *args) {
+ /* TODO */
+ return NULL;
+}
+
+
+/*
+ * Return path of the process executable.
+ */
+static PyObject *
+psutil_proc_exe(PyObject *self, PyObject *args) {
+ pid_t pid;
+ image_info info;
+ int32 cookie = 0;
+ PyObject *py_name;
+ PyObject *py_retlist;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ return NULL;
+ while (get_next_image_info(pid, &cookie, &info) == B_OK) {
+ if (info.type != B_APP_IMAGE)
+ continue;
+ return PyUnicode_DecodeFSDefault(info.name);
+ }
+ return NULL;
+}
+
+
+/*
+ * Return process cmdline as a Python list of cmdline arguments.
+ */
+static PyObject *
+psutil_proc_cmdline(PyObject *self, PyObject *args) {
+ return Py_BuildValue("[]");
+ /* TODO! */
+ pid_t pid;
+ team_info info;
+ PyObject *py_arg;
+ PyObject *py_retlist = NULL;
+
+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
+ return NULL;
+ if (get_team_info(pid, &info) != B_OK)
+ return NULL;
+
+ py_retlist = PyList_New(0);
+ if (py_retlist == NULL)
+ return NULL;
+
+ /* TODO: we can't really differentiate args as we have a single string */
+ /* TODO: try to split? */
+ py_arg = PyUnicode_DecodeFSDefault(info.args);
+ if (!py_arg)
+ goto error;
+ if (PyList_Append(py_retlist, py_arg))
+ goto error;
+
+ return Py_BuildValue("N", py_retlist);
+
+error:
+ Py_XDECREF(py_arg);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+
+
+/*
+ * Return process environment as a Python string.
+ */
+static PyObject *
+psutil_proc_environ(PyObject *self, PyObject *args) {
+ /* TODO: likely impossible */
+ return NULL;
+}
+
+
+/*
+ * Return the number of logical CPUs in the system.
+ */
+static PyObject *
+psutil_cpu_count_logical(PyObject *self, PyObject *args) {
+ /* TODO:get_cpu_topology_info */
+ return NULL;
+}
+
+
+/*
+ * Return the number of physical CPUs in the system.
+ */
+static PyObject *
+psutil_cpu_count_phys(PyObject *self, PyObject *args) {
+ /* TODO:get_cpu_topology_info */
+ return NULL;
+}
+
+
+/*
+ * Returns the USS (unique set size) of the process. Reference:
+ * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/
+ * nsMemoryReporterManager.cpp
+ */
+static PyObject *
+psutil_proc_memory_uss(PyObject *self, PyObject *args) {
+ /* TODO */
+ return NULL;
+}
+
+
+/*
+ * Return system virtual memory stats.
+ * See:
+ * https://opensource.apple.com/source/system_cmds/system_cmds-790/
+ * vm_stat.tproj/vm_stat.c.auto.html
+ */
+static PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+ system_info info;
+ status_t ret;
+ int pagesize = getpagesize();
+
+
+ ret = get_system_info(&info);
+ if (ret != B_OK) {
+ PyErr_Format(
+ PyExc_RuntimeError, "get_system_info() syscall failed: %s",
+ strerror(ret));
+ return NULL;
+ }
+
+ return Py_BuildValue(
+ "KKKKKKK",
+ (unsigned long long) info.max_pages * pagesize, // total
+ (unsigned long long) info.used_pages * pagesize, // used
+ (unsigned long long) info.cached_pages * pagesize, // cached
+ (unsigned long long) info.block_cache_pages * pagesize, // buffers
+ (unsigned long long) info.ignored_pages * pagesize, // ignored
+ (unsigned long long) info.needed_memory, // needed
+ (unsigned long long) info.free_memory // available
+ );
+}
+
+
+/*
+ * Return stats about swap memory.
+ */
+static PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+ system_info info;
+ status_t ret;
+ int pagesize = getpagesize();
+
+
+ ret = get_system_info(&info);
+ if (ret != B_OK) {
+ PyErr_Format(
+ PyExc_RuntimeError, "get_system_info() syscall failed: %s",
+ strerror(ret));
+ return NULL;
+ }
+
+ return Py_BuildValue(
+ "KK",
+ (unsigned long long) info.max_swap_pages * pagesize,
+ (unsigned long long) info.free_swap_pages * pagesize);
+ /* XXX: .page_faults? */
+}
+
+
+/*
+ * Return a Python tuple representing user, kernel and idle CPU times
+ */
+static PyObject *
+psutil_cpu_times(PyObject *self, PyObject *args) {
+ system_info info;
+ status_t ret;
+ int pagesize = getpagesize();
+
+ ret = get_system_info(&info);
+ if (ret != B_OK) {
+ PyErr_Format(
+ PyExc_RuntimeError, "get_system_info() syscall failed: %s",
+ strerror(ret));
+ return NULL;
+ }
+
+ cpu_info cpus[info.cpu_count];
+
+ ret = get_cpu_info(0, info.cpu_count, cpus);
+ if (ret != B_OK) {
+ PyErr_Format(
+ PyExc_RuntimeError, "get_cpu_info() syscall failed: %s",
+ strerror(ret));
+ return NULL;
+ }
+
+ /* TODO */
+ return Py_BuildValue(
+ "(dddd)",
+ (double)0.0,
+ (double)0.0,
+ (double)0.0,
+ (double)0.0
+ );
+}
+
+
+/*
+ * Return a Python list of tuple representing per-cpu times
+ */
+static PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+ system_info info;
+ status_t ret;
+ uint32 i, pagesize = getpagesize();
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_cputime = NULL;
+
+ ret = get_system_info(&info);
+ if (ret != B_OK) {
+ PyErr_Format(
+ PyExc_RuntimeError, "get_system_info() syscall failed: %s",
+ strerror(ret));
+ return NULL;
+ }
+
+ cpu_info cpus[info.cpu_count];
+
+ ret = get_cpu_info(0, info.cpu_count, cpus);
+ if (ret != B_OK) {
+ PyErr_Format(
+ PyExc_RuntimeError, "get_cpu_info() syscall failed: %s",
+ strerror(ret));
+ return NULL;
+ }
+
+ /* TODO: check idle thread times? */
+
+ for (i = 0; i < info.cpu_count; i++) {
+ py_cputime = Py_BuildValue(
+ "(dddd)",
+ (double)0.0,
+ (double)0.0,
+ (double)0.0,
+ (double)0.0
+ );
+ if (!py_cputime)
+ goto error;
+ if (PyList_Append(py_retlist, py_cputime))
+ goto error;
+ Py_CLEAR(py_cputime);
+ }
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_cputime);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+
+
+/*
+ * Retrieve CPU frequency.
+ */
+static PyObject *
+psutil_cpu_freq(PyObject *self, PyObject *args) {
+ status_t ret;
+ uint32 i, topologyNodeCount = 0;
+ ret = get_cpu_topology_info(NULL, &topologyNodeCount);
+ if (ret != B_OK || topologyNodeCount == 0)
+ return NULL;
+ cpu_topology_node_info topology[topologyNodeCount];
+ ret = get_cpu_topology_info(topology, &topologyNodeCount);
+
+ for (i = 0; i < topologyNodeCount; i++) {
+ if (topology[i].type == B_TOPOLOGY_CORE)
+ /* TODO: find min / max? */
+ return Py_BuildValue(
+ "KKK",
+ topology[i].data.core.default_frequency,
+ topology[i].data.core.default_frequency,
+ topology[i].data.core.default_frequency);
+ }
+
+ 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) {
+ system_info info;
+ status_t ret;
+ int pagesize = getpagesize();
+
+ ret = get_system_info(&info);
+ if (ret != B_OK) {
+ PyErr_Format(
+ PyExc_RuntimeError, "get_system_info() syscall failed: %s",
+ strerror(ret));
+ return NULL;
+ }
+
+ return Py_BuildValue("f", (float)info.boot_time / 1000000.0);
+}
+
+
+/*
+ * 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) {
+ int32 cookie = 0;
+ dev_t dev;
+ fs_info info;
+ uint32 flags;
+ BString opts;
+ PyObject *py_dev = NULL;
+ PyObject *py_mountp = NULL;
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ while ((dev = next_dev(&cookie)) >= 0) {
+ opts = "";
+ if (fs_stat_dev(dev, &info) != B_OK)
+ continue;
+ flags = info.flags;
+
+ // see fs_info.h
+ if (flags & B_FS_IS_READONLY)
+ opts << "ro";
+ else
+ opts << "rw";
+ // TODO
+ if (flags & B_FS_IS_REMOVABLE)
+ opts << ",removable";
+ if (flags & B_FS_IS_PERSISTENT)
+ opts << ",persistent";
+ if (flags & B_FS_IS_SHARED)
+ opts << ",shared";
+ if (flags & B_FS_HAS_MIME)
+ opts << ",has_mime";
+ if (flags & B_FS_HAS_ATTR)
+ opts << ",has_attr";
+ if (flags & B_FS_HAS_QUERY)
+ opts << ",has_query";
+ if (flags & B_FS_HAS_SELF_HEALING_LINKS)
+ opts << ",has_self_healing_links";
+ if (flags & B_FS_HAS_ALIASES)
+ opts << ",has_aliases";
+ if (flags & B_FS_SUPPORTS_NODE_MONITORING)
+ opts << ",has_node_monitoring";
+ if (flags & B_FS_SUPPORTS_MONITOR_CHILDREN)
+ opts << ",cmdflags";
+
+ py_dev = PyUnicode_DecodeFSDefault(info.device_name);
+ if (! py_dev)
+ goto error;
+ py_mountp = PyUnicode_DecodeFSDefault(info.volume_name);
+ if (! py_mountp)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(OOss)",
+ py_dev, // device
+ py_mountp, // mount point
+ info.fsh_name, // fs type
+ opts.String()); // options
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_dev);
+ Py_CLEAR(py_mountp);
+ Py_CLEAR(py_tuple);
+ }
+
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_dev);
+ Py_XDECREF(py_mountp);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+
+
+/*
+ * Return process threads
+ */
+static PyObject *
+psutil_proc_threads(PyObject *self, PyObject *args) {
+ pid_t pid;
+ int32 cookie = 0;
+ thread_info info;
+ int err, ret;
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ if (! PyArg_ParseTuple(args, "l", &pid))
+ goto error;
+
+ while (get_next_thread_info(pid, &cookie, &info) == B_OK) {
+ py_tuple = Py_BuildValue(
+ "Iffl",
+ info.thread,
+ (float)info.user_time / 1000000.0,
+ (float)info.kernel_time / 1000000.0,
+ (long)info.state
+ //XXX: priority, ?
+ );
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_tuple);
+ }
+
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
+
+
+/*
+ * Return process open files as a Python tuple.
+ * References:
+ * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/m78fd
+ * - /usr/include/sys/proc_info.h
+ */
+static PyObject *
+psutil_proc_open_files(PyObject *self, PyObject *args) {
+ /* TODO */
+ return NULL;
+}
+
+
+/*
+ * Return process TCP and UDP connections as a list of tuples.
+ * Raises NSP in case of zombie process.
+ * References:
+ * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/wNrC0
+ * - /usr/include/sys/proc_info.h
+ */
+static PyObject *
+psutil_proc_connections(PyObject *self, PyObject *args) {
+ /* TODO */
+ return NULL;
+}
+
+
+/*
+ * Return number of file descriptors opened by process.
+ * Raises NSP in case of zombie process.
+ */
+static PyObject *
+psutil_proc_num_fds(PyObject *self, PyObject *args) {
+ /* TODO */
+ 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) {
+ /* TODO */
+ return NULL;
+}
+
+
+/*
+ * Return a Python dict of tuples for disk I/O information
+ */
+static PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+ /* TODO */
+ return NULL;
+}
+
+
+/*
+ * Return currently connected users as a list of tuples.
+ */
+static PyObject *
+psutil_users(PyObject *self, PyObject *args) {
+ /* TODO */
+ return NULL;
+}
+
+
+/*
+ * Return CPU statistics.
+ */
+static PyObject *
+psutil_cpu_stats(PyObject *self, PyObject *args) {
+ /* TODO */
+ return NULL;
+}
+
+
+/*
+ * Return battery information.
+ */
+static PyObject *
+psutil_sensors_battery(PyObject *self, PyObject *args) {
+ /* TODO */
+ return NULL;
+}
+
+
+/*
+ * define the psutil C module methods and initialize the module.
+ */
+static PyMethodDef mod_methods[] = {
+ // --- per-process functions
+
+ {"proc_team_info_oneshot", psutil_proc_team_info_oneshot, METH_VARARGS,
+ "Return multiple process info."},
+ {"proc_team_usage_info_oneshot", psutil_proc_team_usage_info_oneshot, METH_VARARGS,
+ "Return multiple process info."},
+ {"proc_name", psutil_proc_name, METH_VARARGS,
+ "Return process name"},
+ {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS,
+ "Return process cmdline as a list of cmdline arguments"},
+ {"proc_environ", psutil_proc_environ, METH_VARARGS,
+ "Return process environment data"},
+ {"proc_exe", psutil_proc_exe, METH_VARARGS,
+ "Return path of the process executable"},
+ {"proc_cwd", psutil_proc_cwd, METH_VARARGS,
+ "Return process current working directory."},
+ {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS,
+ "Return process USS memory"},
+ {"proc_threads", psutil_proc_threads, METH_VARARGS,
+ "Return process threads as a list of tuples"},
+ {"proc_open_files", psutil_proc_open_files, METH_VARARGS,
+ "Return files opened by process as a list of tuples"},
+ {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS,
+ "Return the number of fds opened by process."},
+ {"proc_connections", psutil_proc_connections, METH_VARARGS,
+ "Get process TCP and UDP connections as a list of tuples"},
+
+ // --- system-related functions
+
+ {"pids", psutil_pids, METH_VARARGS,
+ "Returns a list of PIDs currently running on the system"},
+ {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS,
+ "Return number of logical CPUs on the system"},
+ {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS,
+ "Return number of physical CPUs on the system"},
+ {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
+ "Return system virtual memory stats"},
+ {"swap_mem", psutil_swap_mem, METH_VARARGS,
+ "Return stats about swap memory, in bytes"},
+ {"cpu_times", psutil_cpu_times, METH_VARARGS,
+ "Return system cpu times as a tuple (user, system, nice, idle, irc)"},
+ {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
+ "Return system per-cpu times as a list of tuples"},
+ {"cpu_freq", psutil_cpu_freq, METH_VARARGS,
+ "Return cpu current frequency"},
+ {"boot_time", psutil_boot_time, METH_VARARGS,
+ "Return the system boot time expressed in seconds since the epoch."},
+ {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
+ "Return a list of tuples including device, mount point and "
+ "fs type for all partitions mounted on the system."},
+ {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
+ "Return dict of tuples of networks I/O information."},
+ {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
+ "Return dict of tuples of disks I/O information."},
+ {"users", psutil_users, METH_VARARGS,
+ "Return currently connected users as a list of tuples"},
+ {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
+ "Return CPU statistics"},
+ {"sensors_battery", psutil_sensors_battery, METH_VARARGS,
+ "Return battery information."},
+
+ // --- others
+ {"set_testing", psutil_set_testing, METH_NOARGS,
+ "Set psutil in testing mode"},
+
+ {NULL, NULL, 0, NULL}
+};
+
+
+#if PY_MAJOR_VERSION >= 3
+ #define INITERR return NULL
+
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "_psutil_haiku",
+ NULL,
+ -1,
+ mod_methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+extern "C" PyObject *PyInit__psutil_haiku(void);
+
+ PyObject *PyInit__psutil_haiku(void)
+#else /* PY_MAJOR_VERSION */
+ #define INITERR return
+
+extern "C" void init_psutil_haiku(void);
+
+ void init_psutil_haiku(void)
+#endif /* PY_MAJOR_VERSION */
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *mod = PyModule_Create(&moduledef);
+#else
+ PyObject *mod = Py_InitModule("_psutil_haiku", mod_methods);
+#endif
+ if (mod == NULL)
+ INITERR;
+
+ if (psutil_setup() != 0)
+ INITERR;
+
+ if (PyModule_AddIntConstant(mod, "version", PSUTIL_VERSION))
+ INITERR;
+ // process status constants, defined in:
+ // headers/os/kernel/OS.h
+ if (PyModule_AddIntConstant(mod, "B_THREAD_RUNNING", B_THREAD_RUNNING))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "B_THREAD_READY", B_THREAD_READY))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "B_THREAD_RECEIVING", B_THREAD_RECEIVING))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "B_THREAD_ASLEEP", B_THREAD_ASLEEP))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "B_THREAD_SUSPENDED", B_THREAD_SUSPENDED))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "B_THREAD_WAITING", B_THREAD_WAITING))
+ INITERR;
+ // connection status constants
+/*XXX:
+ if (PyModule_AddIntConstant(mod, "TCPS_CLOSED", TCPS_CLOSED))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_CLOSING", TCPS_CLOSING))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_LISTEN", TCPS_LISTEN))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_ESTABLISHED", TCPS_ESTABLISHED))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_SYN_SENT", TCPS_SYN_SENT))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_SYN_RECEIVED", TCPS_SYN_RECEIVED))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_LAST_ACK", TCPS_LAST_ACK))
+ INITERR;
+ if (PyModule_AddIntConstant(mod, "TCPS_TIME_WAIT", TCPS_TIME_WAIT))
+ INITERR;
+*/
+ if (PyModule_AddIntConstant(mod, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE))
+ INITERR;
+
+ if (mod == NULL)
+ INITERR;
+#if PY_MAJOR_VERSION >= 3
+ return mod;
+#endif
+}
diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c
index aa60084..8165475 100644
--- a/psutil/_psutil_posix.c
+++ b/psutil/_psutil_posix.c
@@ -40,6 +40,9 @@
#include <sys/sockio.h>
#elif defined(PSUTIL_AIX)
#include <netdb.h>
+#elif defined(PSUTIL_HAIKU)
+ #include <netdb.h>
+ #include <sys/sockio.h>
#endif
#include "_psutil_common.h"
@@ -675,7 +678,7 @@ static PyMethodDef mod_methods[] = {
if (mod == NULL)
INITERR;
-#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) || defined(PSUTIL_SUNOS) || defined(PSUTIL_AIX)
+#if defined(PSUTIL_BSD) || defined(PSUTIL_OSX) || defined(PSUTIL_SUNOS) || defined(PSUTIL_AIX) || defined(PSUTIL_HAIKU)
if (PyModule_AddIntConstant(mod, "AF_LINK", AF_LINK)) INITERR;
#endif
diff --git a/setup.py b/setup.py
index 99818ad..8b81d25 100755
--- a/setup.py
+++ b/setup.py
@@ -32,6 +32,7 @@ sys.path.insert(0, os.path.join(HERE, "psutil"))
from _common import AIX # NOQA
from _common import BSD # NOQA
from _common import FREEBSD # NOQA
+from _common import HAIKU # NOQA
from _common import LINUX # NOQA
from _common import MACOS # NOQA
from _common import NETBSD # NOQA
@@ -256,6 +257,16 @@ elif AIX:
libraries=['perfstat'],
define_macros=macros)
+elif HAIKU:
+ macros.append(("PSUTIL_HAIKU", 1))
+ macros.append(("_DEFAULT_SOURCE", 1))
+ ext = Extension(
+ 'psutil._psutil_haiku',
+ sources=sources + [
+ 'psutil/_psutil_haiku.cpp'],
+ libraries=['be', 'network'],
+ define_macros=macros)
+
else:
sys.exit('platform %s is not supported' % sys.platform)
--
2.24.1