1From 13892923cf7b6b8d8f329e764a40c8eb2e0d2602 Mon Sep 17 00:00:00 2001 2From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= <revol@free.fr> 3Date: Wed, 12 Feb 2020 04:16:40 +0100 4Subject: WIP: preliminary Haiku port 5 6 7diff --git a/psutil/__init__.py b/psutil/__init__.py 8index 8138db4..b326a86 100644 9--- a/psutil/__init__.py 10+++ b/psutil/__init__.py 11@@ -16,6 +16,7 @@ sensors) in Python. Supported platforms: 12 - NetBSD 13 - Sun Solaris 14 - AIX 15+ - Haiku 16 17 Works with Python versions 2.7 and 3.6+. 18 """ 19@@ -55,6 +56,7 @@ from ._common import CONN_SYN_RECV 20 from ._common import CONN_SYN_SENT 21 from ._common import CONN_TIME_WAIT 22 from ._common import FREEBSD # NOQA 23+from ._common import HAIKU 24 from ._common import LINUX 25 from ._common import MACOS 26 from ._common import NETBSD # NOQA 27@@ -141,6 +143,9 @@ elif AIX: 28 # via sys.modules. 29 PROCFS_PATH = "/proc" 30 31+elif HAIKU: 32+ from . import _pshaiku as _psplatform 33+ 34 else: # pragma: no cover 35 raise NotImplementedError('platform %s is not supported' % sys.platform) 36 37diff --git a/psutil/_common.py b/psutil/_common.py 38index 6989fea..34daca1 100644 39--- a/psutil/_common.py 40+++ b/psutil/_common.py 41@@ -50,8 +50,8 @@ _DEFAULT = object() 42 # fmt: off 43 __all__ = [ 44 # OS constants 45- 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX', 46- 'SUNOS', 'WINDOWS', 47+ 'FREEBSD', 'BSD', 'HAIKU', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 48+ 'POSIX', 'SUNOS', 'WINDOWS', 49 # connection constants 50 'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED', 51 'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN', 52@@ -97,6 +97,7 @@ NETBSD = sys.platform.startswith("netbsd") 53 BSD = FREEBSD or OPENBSD or NETBSD 54 SUNOS = sys.platform.startswith(("sunos", "solaris")) 55 AIX = sys.platform.startswith("aix") 56+HAIKU = sys.platform.startswith("haiku") 57 58 59 # =================================================================== 60diff --git a/psutil/_pshaiku.py b/psutil/_pshaiku.py 61new file mode 100644 62index 0000000..89115f7 63--- /dev/null 64+++ b/psutil/_pshaiku.py 65@@ -0,0 +1,433 @@ 66+# Copyright (c) 2009, Giampaolo Rodola' 67+# Copyright (c) 2017, Arnon Yaari 68+# Copyright (c) 2020, Fran��ois Revol 69+# All rights reserved. 70+# Use of this source code is governed by a BSD-style license that can be 71+# found in the LICENSE file. 72+ 73+"""Haiku platform implementation.""" 74+ 75+import functools 76+# import glob 77+import os 78+# import re 79+# import subprocess 80+# import sys 81+from collections import namedtuple 82+ 83+from . import _common 84+from . import _psposix 85+from . import _psutil_haiku as cext 86+from . import _psutil_posix as cext_posix 87+from ._common import AccessDenied 88+# from ._common import conn_to_ntuple 89+from ._common import memoize_when_activated 90+from ._common import NoSuchProcess 91+from ._common import usage_percent 92+from ._common import ZombieProcess 93+from ._compat import FileNotFoundError 94+from ._compat import PermissionError 95+from ._compat import ProcessLookupError 96+# from ._compat import PY3 97+ 98+ 99+__extra__all__ = [] 100+ 101+ 102+# ===================================================================== 103+# --- globals 104+# ===================================================================== 105+ 106+ 107+HAS_NET_IO_COUNTERS = hasattr(cext, "net_io_counters") 108+HAS_PROC_IO_COUNTERS = hasattr(cext, "proc_io_counters") 109+ 110+PAGE_SIZE = os.sysconf('SC_PAGE_SIZE') 111+AF_LINK = cext_posix.AF_LINK 112+ 113+PROC_STATUSES = { 114+ cext.B_THREAD_RUNNING: _common.STATUS_RUNNING, 115+ cext.B_THREAD_READY: _common.STATUS_IDLE, 116+ cext.B_THREAD_RECEIVING: _common.STATUS_WAITING, 117+ cext.B_THREAD_ASLEEP: _common.STATUS_SLEEPING, 118+ cext.B_THREAD_SUSPENDED: _common.STATUS_STOPPED, 119+ cext.B_THREAD_WAITING: _common.STATUS_WAITING, 120+} 121+ 122+team_info_map = dict( 123+ thread_count=0, 124+ image_count=1, 125+ area_count=2, 126+ uid=3, 127+ gid=4, 128+ name=5) 129+ 130+team_usage_info_map = dict( 131+ user_time=0, 132+ kernel_time=1) 133+ 134+thread_info_map = dict( 135+ id=0, 136+ user_time=1, 137+ kernel_time=2, 138+ state=3) 139+ 140+ 141+# ===================================================================== 142+# --- named tuples 143+# ===================================================================== 144+ 145+ 146+# psutil.Process.memory_info() 147+pmem = namedtuple('pmem', ['rss', 'vms']) 148+# psutil.Process.memory_full_info() 149+pfullmem = pmem 150+# psutil.Process.cpu_times() 151+scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait']) 152+# psutil.virtual_memory() 153+svmem = namedtuple('svmem', ['total', 'used', 'percent', 'cached', 'buffers', 154+ 'ignored', 'needed', 'available']) 155+ 156+ 157+# ===================================================================== 158+# --- memory 159+# ===================================================================== 160+ 161+ 162+def virtual_memory(): 163+ total, inuse, cached, buffers, ignored, needed, avail = cext.virtual_mem() 164+ percent = usage_percent((total - avail), total, round_=1) 165+ return svmem(total, inuse, percent, cached, buffers, ignored, needed, 166+ avail) 167+ 168+ 169+def swap_memory(): 170+ """Swap system memory as a (total, used, free, sin, sout) tuple.""" 171+ total, free = cext.swap_mem() 172+ sin = 0 173+ sout = 0 174+ used = total - free 175+ percent = usage_percent(used, total, round_=1) 176+ return _common.sswap(total, used, free, percent, sin, sout) 177+ 178+ 179+# ===================================================================== 180+# --- CPU 181+# ===================================================================== 182+ 183+ 184+def cpu_times(): 185+ """Return system-wide CPU times as a named tuple""" 186+ ret = cext.per_cpu_times() 187+ return scputimes(*[sum(x) for x in zip(*ret)]) 188+ 189+ 190+def per_cpu_times(): 191+ """Return system per-CPU times as a list of named tuples""" 192+ ret = cext.per_cpu_times() 193+ return [scputimes(*x) for x in ret] 194+ 195+ 196+def cpu_count_logical(): 197+ """Return the number of logical CPUs in the system.""" 198+ try: 199+ return os.sysconf("SC_NPROCESSORS_ONLN") 200+ except ValueError: 201+ # mimic os.cpu_count() behavior 202+ return None 203+ 204+ 205+def cpu_count_physical(): 206+ # TODO: 207+ return None 208+ 209+ 210+def cpu_stats(): 211+ """Return various CPU stats as a named tuple.""" 212+ ctx_switches, interrupts, soft_interrupts, syscalls = cext.cpu_stats() 213+ return _common.scpustats( 214+ ctx_switches, interrupts, soft_interrupts, syscalls) 215+ 216+ 217+def cpu_freq(): 218+ """Return CPU frequency. 219+ """ 220+ curr, min_, max_ = cext.cpu_freq() 221+ return [_common.scpufreq(curr, min_, max_)] 222+ 223+ 224+# ===================================================================== 225+# --- disks 226+# ===================================================================== 227+ 228+ 229+disk_io_counters = cext.disk_io_counters 230+disk_usage = _psposix.disk_usage 231+ 232+ 233+def disk_partitions(all=False): 234+ """Return system disk partitions.""" 235+ # TODO - the filtering logic should be better checked so that 236+ # it tries to reflect 'df' as much as possible 237+ retlist = [] 238+ partitions = cext.disk_partitions() 239+ for partition in partitions: 240+ device, mountpoint, fstype, opts = partition 241+ if device == 'none': 242+ device = '' 243+ if not all: 244+ # Differently from, say, Linux, we don't have a list of 245+ # common fs types so the best we can do, AFAIK, is to 246+ # filter by filesystem having a total size > 0. 247+ if not disk_usage(mountpoint).total: 248+ continue 249+ ntuple = _common.sdiskpart(device, mountpoint, fstype, opts) 250+ retlist.append(ntuple) 251+ return retlist 252+ 253+ 254+# ===================================================================== 255+# --- network 256+# ===================================================================== 257+ 258+ 259+net_if_addrs = cext_posix.net_if_addrs 260+ 261+if HAS_NET_IO_COUNTERS: 262+ net_io_counters = cext.net_io_counters 263+ 264+ 265+def net_connections(kind, _pid=-1): 266+ """Return socket connections. If pid == -1 return system-wide 267+ connections (as opposed to connections opened by one process only). 268+ """ 269+ # TODO 270+ return None 271+ 272+ 273+def net_if_stats(): 274+ """Get NIC stats (isup, duplex, speed, mtu).""" 275+ # TODO 276+ return None 277+ 278+ 279+# ===================================================================== 280+# --- other system functions 281+# ===================================================================== 282+ 283+ 284+def boot_time(): 285+ """The system boot time expressed in seconds since the epoch.""" 286+ return cext.boot_time() 287+ 288+ 289+def users(): 290+ """Return currently connected users as a list of namedtuples.""" 291+ retlist = [] 292+ rawlist = cext.users() 293+ localhost = (':0.0', ':0') 294+ for item in rawlist: 295+ user, tty, hostname, tstamp, user_process, pid = item 296+ # note: the underlying C function includes entries about 297+ # system boot, run level and others. We might want 298+ # to use them in the future. 299+ if not user_process: 300+ continue 301+ if hostname in localhost: 302+ hostname = 'localhost' 303+ nt = _common.suser(user, tty, hostname, tstamp, pid) 304+ retlist.append(nt) 305+ return retlist 306+ 307+ 308+# ===================================================================== 309+# --- processes 310+# ===================================================================== 311+ 312+ 313+def pids(): 314+ ls = cext.pids() 315+ return ls 316+ 317+ 318+pid_exists = _psposix.pid_exists 319+ 320+ 321+def wrap_exceptions(fun): 322+ """Call callable into a try/except clause and translate ENOENT, 323+ EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. 324+ """ 325+ @functools.wraps(fun) 326+ def wrapper(self, *args, **kwargs): 327+ try: 328+ return fun(self, *args, **kwargs) 329+ except (FileNotFoundError, ProcessLookupError): 330+ # ENOENT (no such file or directory) gets raised on open(). 331+ # ESRCH (no such process) can get raised on read() if 332+ # process is gone in meantime. 333+ if not pid_exists(self.pid): 334+ raise NoSuchProcess(self.pid, self._name) 335+ else: 336+ raise ZombieProcess(self.pid, self._name, self._ppid) 337+ except PermissionError: 338+ raise AccessDenied(self.pid, self._name) 339+ return wrapper 340+ 341+ 342+class Process(object): 343+ """Wrapper class around underlying C implementation.""" 344+ 345+ __slots__ = ["pid", "_name", "_ppid", "_cache"] 346+ 347+ def __init__(self, pid): 348+ self.pid = pid 349+ self._name = None 350+ self._ppid = None 351+ 352+ def oneshot_enter(self): 353+ self._proc_team_info.cache_activate(self) 354+ self._proc_team_usage_info.cache_activate(self) 355+ 356+ def oneshot_exit(self): 357+ self._proc_team_info.cache_deactivate(self) 358+ self._proc_team_usage_info.cache_deactivate(self) 359+ 360+ @wrap_exceptions 361+ @memoize_when_activated 362+ def _proc_team_info(self): 363+ ret = cext.proc_team_info_oneshot(self.pid) 364+ print("%d %d\n" % (len(ret), len(team_info_map))) 365+ assert len(ret) == len(team_info_map) 366+ return ret 367+ 368+ @wrap_exceptions 369+ @memoize_when_activated 370+ def _proc_team_usage_info(self): 371+ ret = cext.proc_team_usage_info_oneshot(self.pid) 372+ print("%d %d\n" % (len(ret), len(team_usage_info_map))) 373+ assert len(ret) == len(team_usage_info_map) 374+ return ret 375+ 376+ @wrap_exceptions 377+ def name(self): 378+ return cext.proc_name(self.pid).rstrip("\x00") 379+ 380+ @wrap_exceptions 381+ def exe(self): 382+ return cext.proc_exe(self.pid) 383+ 384+ @wrap_exceptions 385+ def cmdline(self): 386+ return cext.proc_cmdline(self.pid) 387+ 388+ @wrap_exceptions 389+ def environ(self): 390+ return cext.proc_environ(self.pid) 391+ 392+ @wrap_exceptions 393+ def create_time(self): 394+ return None 395+ 396+ @wrap_exceptions 397+ def num_threads(self): 398+ return self._proc_team_info()[team_info_map['thread_count']] 399+ 400+ @wrap_exceptions 401+ def threads(self): 402+ rawlist = cext.proc_threads(self.pid) 403+ retlist = [] 404+ for thread_id, utime, stime, state in rawlist: 405+ ntuple = _common.pthread(thread_id, utime, stime) 406+ retlist.append(ntuple) 407+ return retlist 408+ 409+ @wrap_exceptions 410+ def connections(self, kind='inet'): 411+ ret = net_connections(kind, _pid=self.pid) 412+ # The underlying C implementation retrieves all OS connections 413+ # and filters them by PID. At this point we can't tell whether 414+ # an empty list means there were no connections for process or 415+ # process is no longer active so we force NSP in case the PID 416+ # is no longer there. 417+ if not ret: 418+ # will raise NSP if process is gone 419+ os.stat('%s/%s' % (self._procfs_path, self.pid)) 420+ return ret 421+ 422+ @wrap_exceptions 423+ def nice_get(self): 424+ return cext_posix.getpriority(self.pid) 425+ 426+ @wrap_exceptions 427+ def nice_set(self, value): 428+ return cext_posix.setpriority(self.pid, value) 429+ 430+ @wrap_exceptions 431+ def ppid(self): 432+ return None 433+ 434+ @wrap_exceptions 435+ def uids(self): 436+ uid = self._proc_team_info()[team_info_map['uid']] 437+ return _common.puids(uid, uid, uid) 438+ 439+ @wrap_exceptions 440+ def gids(self): 441+ gid = self._proc_team_info()[team_info_map['gid']] 442+ return _common.puids(gid, gid, gid) 443+ 444+ @wrap_exceptions 445+ def cpu_times(self): 446+ _user, _kern = self._proc_team_usage_info() 447+ return _common.pcputimes(_user, _kern, _user, _kern) 448+ 449+ @wrap_exceptions 450+ def terminal(self): 451+ # TODO 452+ return None 453+ 454+ @wrap_exceptions 455+ def cwd(self): 456+ return None 457+ 458+ @wrap_exceptions 459+ def memory_info(self): 460+ # TODO: 461+ rss = 0 462+ vms = 0 463+ return pmem(rss, vms) 464+ 465+ memory_full_info = memory_info 466+ 467+ @wrap_exceptions 468+ def status(self): 469+ threads = cext.proc_threads(self.pid) 470+ code = threads[0][thread_info_map['state']] 471+ # XXX is '?' legit? (we're not supposed to return it anyway) 472+ return PROC_STATUSES.get(code, '?') 473+ 474+ def open_files(self): 475+ # TODO: 476+ return [] 477+ 478+ @wrap_exceptions 479+ def num_fds(self): 480+ # TODO: 481+ return None 482+ 483+ @wrap_exceptions 484+ def num_ctx_switches(self): 485+ return _common.pctxsw( 486+ *cext.proc_num_ctx_switches(self.pid)) 487+ 488+ @wrap_exceptions 489+ def wait(self, timeout=None): 490+ return _psposix.wait_pid(self.pid, timeout, self._name) 491+ 492+ @wrap_exceptions 493+ def io_counters(self): 494+ return _common.pio( 495+ 0, 496+ 0, 497+ -1, 498+ -1) 499diff --git a/psutil/_psutil_haiku.cpp b/psutil/_psutil_haiku.cpp 500new file mode 100644 501index 0000000..f63ba07 502--- /dev/null 503+++ b/psutil/_psutil_haiku.cpp 504@@ -0,0 +1,840 @@ 505+/* 506+ * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. 507+ * Use of this source code is governed by a BSD-style license that can be 508+ * found in the LICENSE file. 509+ * 510+ * macOS platform-specific module methods. 511+ */ 512+ 513+#include <Python.h> 514+#include <assert.h> 515+#include <errno.h> 516+#include <stdbool.h> 517+#include <stdlib.h> 518+#include <stdio.h> 519+#include <arpa/inet.h> 520+#include <net/if_dl.h> 521+#include <pwd.h> 522+ 523+#include <fs_info.h> 524+#include <image.h> 525+#include <OS.h> 526+#include <String.h> 527+ 528+extern "C" { 529+ 530+#include "_psutil_common.h" 531+#include "_psutil_posix.h" 532+//#include "arch/haiku/process_info.h" 533+ 534+} 535+ 536+static PyObject *ZombieProcessError; 537+ 538+ 539+/* 540+ * Return a Python list of all the PIDs running on the system. 541+ */ 542+static PyObject * 543+psutil_pids(PyObject *self, PyObject *args) { 544+ int32 cookie = 0; 545+ team_info info; 546+ PyObject *py_pid = NULL; 547+ PyObject *py_retlist = PyList_New(0); 548+ 549+ if (py_retlist == NULL) 550+ return NULL; 551+ 552+ while (get_next_team_info(&cookie, &info) == B_OK) { 553+ /* quite wasteful: we have all team infos already */ 554+ py_pid = Py_BuildValue("i", info.team); 555+ if (! py_pid) 556+ goto error; 557+ if (PyList_Append(py_retlist, py_pid)) 558+ goto error; 559+ Py_CLEAR(py_pid); 560+ } 561+ 562+ return py_retlist; 563+ 564+error: 565+ Py_XDECREF(py_pid); 566+ Py_DECREF(py_retlist); 567+ return NULL; 568+} 569+ 570+ 571+/* 572+ * Return multiple process info as a Python tuple in one shot by 573+ * using get_team_info() and filling up a team_info struct. 574+ */ 575+static PyObject * 576+psutil_proc_team_info_oneshot(PyObject *self, PyObject *args) { 577+ pid_t pid; 578+ team_info info; 579+ PyObject *py_name; 580+ PyObject *py_retlist; 581+ 582+ if (! PyArg_ParseTuple(args, "l", &pid)) 583+ return NULL; 584+ if (get_team_info(pid, &info) != B_OK) 585+ return NULL; 586+ 587+ py_name = PyUnicode_DecodeFSDefault(info.args); 588+ if (! py_name) { 589+ // Likely a decoding error. We don't want to fail the whole 590+ // operation. The python module may retry with proc_name(). 591+ PyErr_Clear(); 592+ py_name = Py_None; 593+ } 594+ 595+ py_retlist = Py_BuildValue( 596+ "lllllO", 597+ (long)info.thread_count, // (long) thread_count 598+ (long)info.image_count, // (long) image_count 599+ (long)info.area_count, // (long) area_count 600+ (long)info.uid, // (long) uid 601+ (long)info.gid, // (long) gid 602+ py_name // (pystr) name 603+ ); 604+ 605+ if (py_retlist != NULL) { 606+ // XXX shall we decref() also in case of Py_BuildValue() error? 607+ Py_DECREF(py_name); 608+ } 609+ return py_retlist; 610+} 611+ 612+ 613+/* 614+ * Return multiple process info as a Python tuple in one shot by 615+ * using get_team_usage_info() and filling up a team_usage_info struct. 616+ */ 617+static PyObject * 618+psutil_proc_team_usage_info_oneshot(PyObject *self, PyObject *args) { 619+ pid_t pid; 620+ team_usage_info info; 621+ PyObject *py_retlist; 622+ 623+ if (! PyArg_ParseTuple(args, "l", &pid)) 624+ return NULL; 625+ if (get_team_usage_info(pid, B_TEAM_USAGE_SELF, &info) != B_OK) 626+ return NULL; 627+ 628+ py_retlist = Py_BuildValue( 629+ "KK", 630+ (unsigned long long)info.user_time, 631+ (unsigned long long)info.kernel_time 632+ ); 633+ 634+ return py_retlist; 635+} 636+ 637+ 638+/* 639+ * Return process name from kinfo_proc as a Python string. 640+ */ 641+static PyObject * 642+psutil_proc_name(PyObject *self, PyObject *args) { 643+ pid_t pid; 644+ team_info info; 645+ PyObject *py_name; 646+ PyObject *py_retlist; 647+ 648+ if (! PyArg_ParseTuple(args, "l", &pid)) 649+ return NULL; 650+ if (get_team_info(pid, &info) != B_OK) 651+ return NULL; 652+ 653+ return PyUnicode_DecodeFSDefault(info.args); 654+} 655+ 656+ 657+/* 658+ * Return process current working directory. 659+ * Raises NSP in case of zombie process. 660+ */ 661+static PyObject * 662+psutil_proc_cwd(PyObject *self, PyObject *args) { 663+ /* TODO */ 664+ return NULL; 665+} 666+ 667+ 668+/* 669+ * Return path of the process executable. 670+ */ 671+static PyObject * 672+psutil_proc_exe(PyObject *self, PyObject *args) { 673+ pid_t pid; 674+ image_info info; 675+ int32 cookie = 0; 676+ PyObject *py_name; 677+ PyObject *py_retlist; 678+ 679+ if (! PyArg_ParseTuple(args, "l", &pid)) 680+ return NULL; 681+ while (get_next_image_info(pid, &cookie, &info) == B_OK) { 682+ if (info.type != B_APP_IMAGE) 683+ continue; 684+ return PyUnicode_DecodeFSDefault(info.name); 685+ } 686+ return NULL; 687+} 688+ 689+ 690+/* 691+ * Return process cmdline as a Python list of cmdline arguments. 692+ */ 693+static PyObject * 694+psutil_proc_cmdline(PyObject *self, PyObject *args) { 695+ return Py_BuildValue("[]"); 696+ /* TODO! */ 697+ pid_t pid; 698+ team_info info; 699+ PyObject *py_arg; 700+ PyObject *py_retlist = NULL; 701+ 702+ if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) 703+ return NULL; 704+ if (get_team_info(pid, &info) != B_OK) 705+ return NULL; 706+ 707+ py_retlist = PyList_New(0); 708+ if (py_retlist == NULL) 709+ return NULL; 710+ 711+ /* TODO: we can't really differentiate args as we have a single string */ 712+ /* TODO: try to split? */ 713+ py_arg = PyUnicode_DecodeFSDefault(info.args); 714+ if (!py_arg) 715+ goto error; 716+ if (PyList_Append(py_retlist, py_arg)) 717+ goto error; 718+ 719+ return Py_BuildValue("N", py_retlist); 720+ 721+error: 722+ Py_XDECREF(py_arg); 723+ Py_DECREF(py_retlist); 724+ return NULL; 725+} 726+ 727+ 728+/* 729+ * Return process environment as a Python string. 730+ */ 731+static PyObject * 732+psutil_proc_environ(PyObject *self, PyObject *args) { 733+ /* TODO: likely impossible */ 734+ return NULL; 735+} 736+ 737+ 738+/* 739+ * Return the number of logical CPUs in the system. 740+ */ 741+static PyObject * 742+psutil_cpu_count_logical(PyObject *self, PyObject *args) { 743+ /* TODO:get_cpu_topology_info */ 744+ return NULL; 745+} 746+ 747+ 748+/* 749+ * Return the number of physical CPUs in the system. 750+ */ 751+static PyObject * 752+psutil_cpu_count_phys(PyObject *self, PyObject *args) { 753+ /* TODO:get_cpu_topology_info */ 754+ return NULL; 755+} 756+ 757+ 758+/* 759+ * Returns the USS (unique set size) of the process. Reference: 760+ * https://dxr.mozilla.org/mozilla-central/source/xpcom/base/ 761+ * nsMemoryReporterManager.cpp 762+ */ 763+static PyObject * 764+psutil_proc_memory_uss(PyObject *self, PyObject *args) { 765+ /* TODO */ 766+ return NULL; 767+} 768+ 769+ 770+/* 771+ * Return system virtual memory stats. 772+ * See: 773+ * https://opensource.apple.com/source/system_cmds/system_cmds-790/ 774+ * vm_stat.tproj/vm_stat.c.auto.html 775+ */ 776+static PyObject * 777+psutil_virtual_mem(PyObject *self, PyObject *args) { 778+ system_info info; 779+ status_t ret; 780+ int pagesize = getpagesize(); 781+ 782+ 783+ ret = get_system_info(&info); 784+ if (ret != B_OK) { 785+ PyErr_Format( 786+ PyExc_RuntimeError, "get_system_info() syscall failed: %s", 787+ strerror(ret)); 788+ return NULL; 789+ } 790+ 791+ return Py_BuildValue( 792+ "KKKKKKK", 793+ (unsigned long long) info.max_pages * pagesize, // total 794+ (unsigned long long) info.used_pages * pagesize, // used 795+ (unsigned long long) info.cached_pages * pagesize, // cached 796+ (unsigned long long) info.block_cache_pages * pagesize, // buffers 797+ (unsigned long long) info.ignored_pages * pagesize, // ignored 798+ (unsigned long long) info.needed_memory, // needed 799+ (unsigned long long) info.free_memory // available 800+ ); 801+} 802+ 803+ 804+/* 805+ * Return stats about swap memory. 806+ */ 807+static PyObject * 808+psutil_swap_mem(PyObject *self, PyObject *args) { 809+ system_info info; 810+ status_t ret; 811+ int pagesize = getpagesize(); 812+ 813+ 814+ ret = get_system_info(&info); 815+ if (ret != B_OK) { 816+ PyErr_Format( 817+ PyExc_RuntimeError, "get_system_info() syscall failed: %s", 818+ strerror(ret)); 819+ return NULL; 820+ } 821+ 822+ return Py_BuildValue( 823+ "KK", 824+ (unsigned long long) info.max_swap_pages * pagesize, 825+ (unsigned long long) info.free_swap_pages * pagesize); 826+ /* XXX: .page_faults? */ 827+} 828+ 829+ 830+/* 831+ * Return a Python tuple representing user, kernel and idle CPU times 832+ */ 833+static PyObject * 834+psutil_cpu_times(PyObject *self, PyObject *args) { 835+ system_info info; 836+ status_t ret; 837+ int pagesize = getpagesize(); 838+ 839+ ret = get_system_info(&info); 840+ if (ret != B_OK) { 841+ PyErr_Format( 842+ PyExc_RuntimeError, "get_system_info() syscall failed: %s", 843+ strerror(ret)); 844+ return NULL; 845+ } 846+ 847+ cpu_info cpus[info.cpu_count]; 848+ 849+ ret = get_cpu_info(0, info.cpu_count, cpus); 850+ if (ret != B_OK) { 851+ PyErr_Format( 852+ PyExc_RuntimeError, "get_cpu_info() syscall failed: %s", 853+ strerror(ret)); 854+ return NULL; 855+ } 856+ 857+ /* TODO */ 858+ return Py_BuildValue( 859+ "(dddd)", 860+ (double)0.0, 861+ (double)0.0, 862+ (double)0.0, 863+ (double)0.0 864+ ); 865+} 866+ 867+ 868+/* 869+ * Return a Python list of tuple representing per-cpu times 870+ */ 871+static PyObject * 872+psutil_per_cpu_times(PyObject *self, PyObject *args) { 873+ system_info info; 874+ status_t ret; 875+ uint32 i, pagesize = getpagesize(); 876+ PyObject *py_retlist = PyList_New(0); 877+ PyObject *py_cputime = NULL; 878+ 879+ ret = get_system_info(&info); 880+ if (ret != B_OK) { 881+ PyErr_Format( 882+ PyExc_RuntimeError, "get_system_info() syscall failed: %s", 883+ strerror(ret)); 884+ return NULL; 885+ } 886+ 887+ cpu_info cpus[info.cpu_count]; 888+ 889+ ret = get_cpu_info(0, info.cpu_count, cpus); 890+ if (ret != B_OK) { 891+ PyErr_Format( 892+ PyExc_RuntimeError, "get_cpu_info() syscall failed: %s", 893+ strerror(ret)); 894+ return NULL; 895+ } 896+ 897+ /* TODO: check idle thread times? */ 898+ 899+ for (i = 0; i < info.cpu_count; i++) { 900+ py_cputime = Py_BuildValue( 901+ "(dddd)", 902+ (double)0.0, 903+ (double)0.0, 904+ (double)0.0, 905+ (double)0.0 906+ ); 907+ if (!py_cputime) 908+ goto error; 909+ if (PyList_Append(py_retlist, py_cputime)) 910+ goto error; 911+ Py_CLEAR(py_cputime); 912+ } 913+ return py_retlist; 914+ 915+error: 916+ Py_XDECREF(py_cputime); 917+ Py_DECREF(py_retlist); 918+ return NULL; 919+} 920+ 921+ 922+/* 923+ * Retrieve CPU frequency. 924+ */ 925+static PyObject * 926+psutil_cpu_freq(PyObject *self, PyObject *args) { 927+ status_t ret; 928+ uint32 i, topologyNodeCount = 0; 929+ ret = get_cpu_topology_info(NULL, &topologyNodeCount); 930+ if (ret != B_OK || topologyNodeCount == 0) 931+ return NULL; 932+ cpu_topology_node_info topology[topologyNodeCount]; 933+ ret = get_cpu_topology_info(topology, &topologyNodeCount); 934+ 935+ for (i = 0; i < topologyNodeCount; i++) { 936+ if (topology[i].type == B_TOPOLOGY_CORE) 937+ /* TODO: find min / max? */ 938+ return Py_BuildValue( 939+ "KKK", 940+ topology[i].data.core.default_frequency, 941+ topology[i].data.core.default_frequency, 942+ topology[i].data.core.default_frequency); 943+ } 944+ 945+ return NULL; 946+} 947+ 948+ 949+/* 950+ * Return a Python float indicating the system boot time expressed in 951+ * seconds since the epoch. 952+ */ 953+static PyObject * 954+psutil_boot_time(PyObject *self, PyObject *args) { 955+ system_info info; 956+ status_t ret; 957+ int pagesize = getpagesize(); 958+ 959+ ret = get_system_info(&info); 960+ if (ret != B_OK) { 961+ PyErr_Format( 962+ PyExc_RuntimeError, "get_system_info() syscall failed: %s", 963+ strerror(ret)); 964+ return NULL; 965+ } 966+ 967+ return Py_BuildValue("f", (float)info.boot_time / 1000000.0); 968+} 969+ 970+ 971+/* 972+ * Return a list of tuples including device, mount point and fs type 973+ * for all partitions mounted on the system. 974+ */ 975+static PyObject * 976+psutil_disk_partitions(PyObject *self, PyObject *args) { 977+ int32 cookie = 0; 978+ dev_t dev; 979+ fs_info info; 980+ uint32 flags; 981+ BString opts; 982+ PyObject *py_dev = NULL; 983+ PyObject *py_mountp = NULL; 984+ PyObject *py_tuple = NULL; 985+ PyObject *py_retlist = PyList_New(0); 986+ 987+ if (py_retlist == NULL) 988+ return NULL; 989+ 990+ while ((dev = next_dev(&cookie)) >= 0) { 991+ opts = ""; 992+ if (fs_stat_dev(dev, &info) != B_OK) 993+ continue; 994+ flags = info.flags; 995+ 996+ // see fs_info.h 997+ if (flags & B_FS_IS_READONLY) 998+ opts << "ro"; 999+ else 1000+ opts << "rw"; 1001+ // TODO 1002+ if (flags & B_FS_IS_REMOVABLE) 1003+ opts << ",removable"; 1004+ if (flags & B_FS_IS_PERSISTENT) 1005+ opts << ",persistent"; 1006+ if (flags & B_FS_IS_SHARED) 1007+ opts << ",shared"; 1008+ if (flags & B_FS_HAS_MIME) 1009+ opts << ",has_mime"; 1010+ if (flags & B_FS_HAS_ATTR) 1011+ opts << ",has_attr"; 1012+ if (flags & B_FS_HAS_QUERY) 1013+ opts << ",has_query"; 1014+ if (flags & B_FS_HAS_SELF_HEALING_LINKS) 1015+ opts << ",has_self_healing_links"; 1016+ if (flags & B_FS_HAS_ALIASES) 1017+ opts << ",has_aliases"; 1018+ if (flags & B_FS_SUPPORTS_NODE_MONITORING) 1019+ opts << ",has_node_monitoring"; 1020+ if (flags & B_FS_SUPPORTS_MONITOR_CHILDREN) 1021+ opts << ",cmdflags"; 1022+ 1023+ py_dev = PyUnicode_DecodeFSDefault(info.device_name); 1024+ if (! py_dev) 1025+ goto error; 1026+ py_mountp = PyUnicode_DecodeFSDefault(info.volume_name); 1027+ if (! py_mountp) 1028+ goto error; 1029+ py_tuple = Py_BuildValue( 1030+ "(OOss)", 1031+ py_dev, // device 1032+ py_mountp, // mount point 1033+ info.fsh_name, // fs type 1034+ opts.String()); // options 1035+ if (!py_tuple) 1036+ goto error; 1037+ if (PyList_Append(py_retlist, py_tuple)) 1038+ goto error; 1039+ Py_CLEAR(py_dev); 1040+ Py_CLEAR(py_mountp); 1041+ Py_CLEAR(py_tuple); 1042+ } 1043+ 1044+ return py_retlist; 1045+ 1046+error: 1047+ Py_XDECREF(py_dev); 1048+ Py_XDECREF(py_mountp); 1049+ Py_XDECREF(py_tuple); 1050+ Py_DECREF(py_retlist); 1051+ return NULL; 1052+} 1053+ 1054+ 1055+/* 1056+ * Return process threads 1057+ */ 1058+static PyObject * 1059+psutil_proc_threads(PyObject *self, PyObject *args) { 1060+ pid_t pid; 1061+ int32 cookie = 0; 1062+ thread_info info; 1063+ int err, ret; 1064+ PyObject *py_tuple = NULL; 1065+ PyObject *py_retlist = PyList_New(0); 1066+ 1067+ if (py_retlist == NULL) 1068+ return NULL; 1069+ 1070+ if (! PyArg_ParseTuple(args, "l", &pid)) 1071+ goto error; 1072+ 1073+ while (get_next_thread_info(pid, &cookie, &info) == B_OK) { 1074+ py_tuple = Py_BuildValue( 1075+ "Iffl", 1076+ info.thread, 1077+ (float)info.user_time / 1000000.0, 1078+ (float)info.kernel_time / 1000000.0, 1079+ (long)info.state 1080+ //XXX: priority, ? 1081+ ); 1082+ if (!py_tuple) 1083+ goto error; 1084+ if (PyList_Append(py_retlist, py_tuple)) 1085+ goto error; 1086+ Py_CLEAR(py_tuple); 1087+ } 1088+ 1089+ return py_retlist; 1090+ 1091+error: 1092+ Py_XDECREF(py_tuple); 1093+ Py_DECREF(py_retlist); 1094+ return NULL; 1095+} 1096+ 1097+ 1098+/* 1099+ * Return process open files as a Python tuple. 1100+ * References: 1101+ * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/m78fd 1102+ * - /usr/include/sys/proc_info.h 1103+ */ 1104+static PyObject * 1105+psutil_proc_open_files(PyObject *self, PyObject *args) { 1106+ /* TODO */ 1107+ return NULL; 1108+} 1109+ 1110+ 1111+/* 1112+ * Return process TCP and UDP connections as a list of tuples. 1113+ * Raises NSP in case of zombie process. 1114+ * References: 1115+ * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/wNrC0 1116+ * - /usr/include/sys/proc_info.h 1117+ */ 1118+static PyObject * 1119+psutil_proc_connections(PyObject *self, PyObject *args) { 1120+ /* TODO */ 1121+ return NULL; 1122+} 1123+ 1124+ 1125+/* 1126+ * Return number of file descriptors opened by process. 1127+ * Raises NSP in case of zombie process. 1128+ */ 1129+static PyObject * 1130+psutil_proc_num_fds(PyObject *self, PyObject *args) { 1131+ /* TODO */ 1132+ return NULL; 1133+} 1134+ 1135+ 1136+/* 1137+ * Return a Python list of named tuples with overall network I/O information 1138+ */ 1139+static PyObject * 1140+psutil_net_io_counters(PyObject *self, PyObject *args) { 1141+ /* TODO */ 1142+ return NULL; 1143+} 1144+ 1145+ 1146+/* 1147+ * Return a Python dict of tuples for disk I/O information 1148+ */ 1149+static PyObject * 1150+psutil_disk_io_counters(PyObject *self, PyObject *args) { 1151+ /* TODO */ 1152+ return NULL; 1153+} 1154+ 1155+ 1156+/* 1157+ * Return currently connected users as a list of tuples. 1158+ */ 1159+static PyObject * 1160+psutil_users(PyObject *self, PyObject *args) { 1161+ /* TODO */ 1162+ return NULL; 1163+} 1164+ 1165+ 1166+/* 1167+ * Return CPU statistics. 1168+ */ 1169+static PyObject * 1170+psutil_cpu_stats(PyObject *self, PyObject *args) { 1171+ /* TODO */ 1172+ return NULL; 1173+} 1174+ 1175+ 1176+/* 1177+ * Return battery information. 1178+ */ 1179+static PyObject * 1180+psutil_sensors_battery(PyObject *self, PyObject *args) { 1181+ /* TODO */ 1182+ return NULL; 1183+} 1184+ 1185+ 1186+/* 1187+ * define the psutil C module methods and initialize the module. 1188+ */ 1189+static PyMethodDef mod_methods[] = { 1190+ // --- per-process functions 1191+ 1192+ {"proc_team_info_oneshot", psutil_proc_team_info_oneshot, METH_VARARGS, 1193+ "Return multiple process info."}, 1194+ {"proc_team_usage_info_oneshot", psutil_proc_team_usage_info_oneshot, METH_VARARGS, 1195+ "Return multiple process info."}, 1196+ {"proc_name", psutil_proc_name, METH_VARARGS, 1197+ "Return process name"}, 1198+ {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS, 1199+ "Return process cmdline as a list of cmdline arguments"}, 1200+ {"proc_environ", psutil_proc_environ, METH_VARARGS, 1201+ "Return process environment data"}, 1202+ {"proc_exe", psutil_proc_exe, METH_VARARGS, 1203+ "Return path of the process executable"}, 1204+ {"proc_cwd", psutil_proc_cwd, METH_VARARGS, 1205+ "Return process current working directory."}, 1206+ {"proc_memory_uss", psutil_proc_memory_uss, METH_VARARGS, 1207+ "Return process USS memory"}, 1208+ {"proc_threads", psutil_proc_threads, METH_VARARGS, 1209+ "Return process threads as a list of tuples"}, 1210+ {"proc_open_files", psutil_proc_open_files, METH_VARARGS, 1211+ "Return files opened by process as a list of tuples"}, 1212+ {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS, 1213+ "Return the number of fds opened by process."}, 1214+ {"proc_connections", psutil_proc_connections, METH_VARARGS, 1215+ "Get process TCP and UDP connections as a list of tuples"}, 1216+ 1217+ // --- system-related functions 1218+ 1219+ {"pids", psutil_pids, METH_VARARGS, 1220+ "Returns a list of PIDs currently running on the system"}, 1221+ {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS, 1222+ "Return number of logical CPUs on the system"}, 1223+ {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS, 1224+ "Return number of physical CPUs on the system"}, 1225+ {"virtual_mem", psutil_virtual_mem, METH_VARARGS, 1226+ "Return system virtual memory stats"}, 1227+ {"swap_mem", psutil_swap_mem, METH_VARARGS, 1228+ "Return stats about swap memory, in bytes"}, 1229+ {"cpu_times", psutil_cpu_times, METH_VARARGS, 1230+ "Return system cpu times as a tuple (user, system, nice, idle, irc)"}, 1231+ {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS, 1232+ "Return system per-cpu times as a list of tuples"}, 1233+ {"cpu_freq", psutil_cpu_freq, METH_VARARGS, 1234+ "Return cpu current frequency"}, 1235+ {"boot_time", psutil_boot_time, METH_VARARGS, 1236+ "Return the system boot time expressed in seconds since the epoch."}, 1237+ {"disk_partitions", psutil_disk_partitions, METH_VARARGS, 1238+ "Return a list of tuples including device, mount point and " 1239+ "fs type for all partitions mounted on the system."}, 1240+ {"net_io_counters", psutil_net_io_counters, METH_VARARGS, 1241+ "Return dict of tuples of networks I/O information."}, 1242+ {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS, 1243+ "Return dict of tuples of disks I/O information."}, 1244+ {"users", psutil_users, METH_VARARGS, 1245+ "Return currently connected users as a list of tuples"}, 1246+ {"cpu_stats", psutil_cpu_stats, METH_VARARGS, 1247+ "Return CPU statistics"}, 1248+ {"sensors_battery", psutil_sensors_battery, METH_VARARGS, 1249+ "Return battery information."}, 1250+ 1251+ // --- others 1252+ {"check_pid_range", psutil_check_pid_range, METH_VARARGS}, 1253+ {"set_debug", psutil_set_debug, METH_VARARGS}, 1254+ {NULL, NULL, 0, NULL} 1255+}; 1256+ 1257+ 1258+#if PY_MAJOR_VERSION >= 3 1259+ #define INITERR return NULL 1260+ 1261+ static struct PyModuleDef moduledef = { 1262+ PyModuleDef_HEAD_INIT, 1263+ "_psutil_haiku", 1264+ NULL, 1265+ -1, 1266+ mod_methods, 1267+ NULL, 1268+ NULL, 1269+ NULL, 1270+ NULL 1271+ }; 1272+ 1273+extern "C" PyObject *PyInit__psutil_haiku(void); 1274+ 1275+ PyObject *PyInit__psutil_haiku(void) 1276+#else /* PY_MAJOR_VERSION */ 1277+ #define INITERR return 1278+ 1279+extern "C" void init_psutil_haiku(void); 1280+ 1281+ void init_psutil_haiku(void) 1282+#endif /* PY_MAJOR_VERSION */ 1283+{ 1284+#if PY_MAJOR_VERSION >= 3 1285+ PyObject *mod = PyModule_Create(&moduledef); 1286+#else 1287+ PyObject *mod = Py_InitModule("_psutil_haiku", mod_methods); 1288+#endif 1289+ if (mod == NULL) 1290+ INITERR; 1291+ 1292+ if (psutil_setup() != 0) 1293+ INITERR; 1294+ 1295+ if (PyModule_AddIntConstant(mod, "version", PSUTIL_VERSION)) 1296+ INITERR; 1297+ // process status constants, defined in: 1298+ // headers/os/kernel/OS.h 1299+ if (PyModule_AddIntConstant(mod, "B_THREAD_RUNNING", B_THREAD_RUNNING)) 1300+ INITERR; 1301+ if (PyModule_AddIntConstant(mod, "B_THREAD_READY", B_THREAD_READY)) 1302+ INITERR; 1303+ if (PyModule_AddIntConstant(mod, "B_THREAD_RECEIVING", B_THREAD_RECEIVING)) 1304+ INITERR; 1305+ if (PyModule_AddIntConstant(mod, "B_THREAD_ASLEEP", B_THREAD_ASLEEP)) 1306+ INITERR; 1307+ if (PyModule_AddIntConstant(mod, "B_THREAD_SUSPENDED", B_THREAD_SUSPENDED)) 1308+ INITERR; 1309+ if (PyModule_AddIntConstant(mod, "B_THREAD_WAITING", B_THREAD_WAITING)) 1310+ INITERR; 1311+ // connection status constants 1312+/*XXX: 1313+ if (PyModule_AddIntConstant(mod, "TCPS_CLOSED", TCPS_CLOSED)) 1314+ INITERR; 1315+ if (PyModule_AddIntConstant(mod, "TCPS_CLOSING", TCPS_CLOSING)) 1316+ INITERR; 1317+ if (PyModule_AddIntConstant(mod, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT)) 1318+ INITERR; 1319+ if (PyModule_AddIntConstant(mod, "TCPS_LISTEN", TCPS_LISTEN)) 1320+ INITERR; 1321+ if (PyModule_AddIntConstant(mod, "TCPS_ESTABLISHED", TCPS_ESTABLISHED)) 1322+ INITERR; 1323+ if (PyModule_AddIntConstant(mod, "TCPS_SYN_SENT", TCPS_SYN_SENT)) 1324+ INITERR; 1325+ if (PyModule_AddIntConstant(mod, "TCPS_SYN_RECEIVED", TCPS_SYN_RECEIVED)) 1326+ INITERR; 1327+ if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1)) 1328+ INITERR; 1329+ if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2)) 1330+ INITERR; 1331+ if (PyModule_AddIntConstant(mod, "TCPS_LAST_ACK", TCPS_LAST_ACK)) 1332+ INITERR; 1333+ if (PyModule_AddIntConstant(mod, "TCPS_TIME_WAIT", TCPS_TIME_WAIT)) 1334+ INITERR; 1335+*/ 1336+ if (PyModule_AddIntConstant(mod, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE)) 1337+ INITERR; 1338+ 1339+ if (mod == NULL) 1340+ INITERR; 1341+#if PY_MAJOR_VERSION >= 3 1342+ return mod; 1343+#endif 1344+} 1345diff --git a/psutil/_psutil_posix.c b/psutil/_psutil_posix.c 1346index 24628af..bd40126 100644 1347--- a/psutil/_psutil_posix.c 1348+++ b/psutil/_psutil_posix.c 1349@@ -45,6 +45,11 @@ 1350 #if defined(PSUTIL_AIX) 1351 #include <netdb.h> 1352 #endif 1353+#if defined(PSUTIL_HAIKU) 1354+ #include <netdb.h> 1355+ #include <sys/sockio.h> 1356+ #define IFF_RUNNING 0x0001 1357+#endif 1358 #if defined(PSUTIL_LINUX) || defined(PSUTIL_FREEBSD) 1359 #include <sys/resource.h> 1360 #endif 1361@@ -916,7 +921,8 @@ static PyMethodDef mod_methods[] = { 1362 #if defined(PSUTIL_BSD) || \ 1363 defined(PSUTIL_OSX) || \ 1364 defined(PSUTIL_SUNOS) || \ 1365- defined(PSUTIL_AIX) 1366+ defined(PSUTIL_AIX) || \ 1367+ defined(PSUTIL_HAIKU) 1368 if (PyModule_AddIntConstant(mod, "AF_LINK", AF_LINK)) INITERR; 1369 #endif 1370 1371diff --git a/setup.py b/setup.py 1372index 7c59f56..32ff198 100755 1373--- a/setup.py 1374+++ b/setup.py 1375@@ -48,6 +48,7 @@ sys.path.insert(0, os.path.join(HERE, "psutil")) 1376 from _common import AIX # NOQA 1377 from _common import BSD # NOQA 1378 from _common import FREEBSD # NOQA 1379+from _common import HAIKU # NOQA 1380 from _common import LINUX # NOQA 1381 from _common import MACOS # NOQA 1382 from _common import NETBSD # NOQA 1383@@ -382,6 +383,16 @@ elif AIX: 1384 # fmt: on 1385 ) 1386 1387+elif HAIKU: 1388+ macros.append(("PSUTIL_HAIKU", 1)) 1389+ macros.append(("_DEFAULT_SOURCE", 1)) 1390+ ext = Extension( 1391+ 'psutil._psutil_haiku', 1392+ sources=sources + [ 1393+ 'psutil/_psutil_haiku.cpp'], 1394+ libraries=['be', 'network'], 1395+ define_macros=macros) 1396+ 1397 else: 1398 sys.exit('platform %s is not supported' % sys.platform) 1399 1400-- 14012.43.2 1402 1403