diff --git psutil/__init__.py psutil/__init__.py index ca2d9273..26a567ec 100644 --- psutil/__init__.py +++ psutil/__init__.py @@ -14,6 +14,7 @@ sensors) in Python. Supported platforms: - FreeBSD - OpenBSD - NetBSD + - DragonFly BSD - Sun Solaris - AIX @@ -55,6 +56,7 @@ from ._common import CONN_SYN_RECV from ._common import CONN_SYN_SENT from ._common import CONN_TIME_WAIT from ._common import FREEBSD # NOQA +from ._common import DRAGONFLY # NOQA from ._common import LINUX from ._common import MACOS from ._common import NETBSD # NOQA @@ -170,7 +172,7 @@ __all__ = [ "POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED", "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "MACOS", "OSX", "POSIX", - "SUNOS", "WINDOWS", "AIX", + "DRAGONFLY", "SUNOS", "WINDOWS", "AIX", # "RLIM_INFINITY", "RLIMIT_AS", "RLIMIT_CORE", "RLIMIT_CPU", "RLIMIT_DATA", # "RLIMIT_FSIZE", "RLIMIT_LOCKS", "RLIMIT_MEMLOCK", "RLIMIT_NOFILE", diff --git psutil/_common.py psutil/_common.py index 3414e8ca..d9894b5c 100644 --- psutil/_common.py +++ psutil/_common.py @@ -49,7 +49,7 @@ _DEFAULT = object() __all__ = [ # OS constants 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX', - 'SUNOS', 'WINDOWS', + 'DRAGONFLY', 'SUNOS', 'WINDOWS', # connection constants 'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED', 'CONN_FIN_WAIT1', 'CONN_FIN_WAIT2', 'CONN_LAST_ACK', 'CONN_LISTEN', @@ -91,7 +91,8 @@ OSX = MACOS # deprecated alias FREEBSD = sys.platform.startswith(("freebsd", "midnightbsd")) OPENBSD = sys.platform.startswith("openbsd") NETBSD = sys.platform.startswith("netbsd") -BSD = FREEBSD or OPENBSD or NETBSD +DRAGONFLY = sys.platform.startswith("dragonfly") +BSD = FREEBSD or OPENBSD or NETBSD or DRAGONFLY SUNOS = sys.platform.startswith(("sunos", "solaris")) AIX = sys.platform.startswith("aix") @@ -116,6 +117,7 @@ STATUS_LOCKED = "locked" # FreeBSD STATUS_WAITING = "waiting" # FreeBSD STATUS_SUSPENDED = "suspended" # NetBSD STATUS_PARKED = "parked" # Linux +STATUS_CORE = "core" # DragonFly # Process.connections() and psutil.net_connections() CONN_ESTABLISHED = "ESTABLISHED" diff --git psutil/_psbsd.py psutil/_psbsd.py index a25c96cd..c886d56c 100644 --- psutil/_psbsd.py +++ psutil/_psbsd.py @@ -19,6 +19,7 @@ from . import _psutil_posix as cext_posix from ._common import FREEBSD from ._common import NETBSD from ._common import OPENBSD +from ._common import DRAGONFLY from ._common import AccessDenied from ._common import NoSuchProcess from ._common import ZombieProcess @@ -51,6 +52,14 @@ if FREEBSD: cext.SWAIT: _common.STATUS_WAITING, cext.SLOCK: _common.STATUS_LOCKED, } +elif DRAGONFLY: + PROC_STATUSES = { + cext.SIDL: _common.STATUS_IDLE, + cext.SACTIVE: _common.STATUS_RUNNING, + cext.SSTOP: _common.STATUS_STOPPED, + cext.SZOMB: _common.STATUS_ZOMBIE, + cext.SCORE: _common.STATUS_CORE, + } elif OPENBSD: PROC_STATUSES = { cext.SIDL: _common.STATUS_IDLE, @@ -166,6 +175,10 @@ if FREEBSD: 'read_bytes', 'write_bytes', 'read_time', 'write_time', 'busy_time']) +elif DRAGONFLY: + sdiskio = namedtuple('sdiskio', ['read_count', 'write_count', + 'read_bytes', 'write_bytes', + 'busy_time']) else: sdiskio = namedtuple('sdiskio', ['read_count', 'write_count', 'read_bytes', 'write_bytes']) @@ -254,6 +267,9 @@ if OPENBSD or NETBSD: def cpu_count_cores(): # OpenBSD and NetBSD do not implement this. return 1 if cpu_count_logical() == 1 else None +elif DRAGONFLY: + def cpu_count_cores(): + return cext.cpu_count_cores() else: def cpu_count_cores(): """Return the number of CPU cores in the system.""" @@ -284,7 +300,7 @@ else: def cpu_stats(): """Return various CPU stats as a named tuple.""" - if FREEBSD: + if FREEBSD or DRAGONFLY: # Note: the C ext is returning some metrics we are not exposing: # traps. ctxsw, intrs, soft_intrs, syscalls, traps = cext.cpu_stats() @@ -774,7 +790,7 @@ class Process(object): raise ValueError("invalid %r kind argument; choose between %s" % (kind, ', '.join([repr(x) for x in conn_tmap]))) - if NETBSD: + if NETBSD or DRAGONFLY: families, types = conn_tmap[kind] ret = [] rawlist = cext.net_connections(self.pid) diff --git psutil/_psutil_bsd.c psutil/_psutil_bsd.c index 1ffa7b00..8e7c123b 100644 --- psutil/_psutil_bsd.c +++ psutil/_psutil_bsd.c @@ -101,6 +101,17 @@ #ifndef DTYPE_VNODE #define DTYPE_VNODE 1 #endif +#elif PSUTIL_DRAGONFLY + #include "arch/dragonfly/cpu.h" + #include "arch/dragonfly/mem.h" + #include "arch/dragonfly/disk.h" + #include "arch/dragonfly/sensors.h" + #include "arch/dragonfly/proc.h" + #include "arch/dragonfly/sys_socks.h" + #include "arch/dragonfly/proc_socks.h" + #include + + #include #endif @@ -135,11 +146,17 @@ psutil_pids(PyObject *self, PyObject *args) { for (idx = 0; idx < num_processes; idx++) { #ifdef PSUTIL_FREEBSD py_pid = PyLong_FromPid(proclist->ki_pid); +#elif defined(PSUTIL_DRAGONFLY) + py_pid = PyLong_FromPid(proclist->kp_pid); #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) py_pid = PyLong_FromPid(proclist->p_pid); #endif if (!py_pid) goto error; +#ifdef PSUTIL_DRAGONFLY + /* TODO(tuxillo): Kernel threads not included for now */ + if (proclist->kp_pid != -1) +#endif if (PyList_Append(py_retlist, py_pid)) goto error; Py_CLEAR(py_pid); @@ -204,6 +221,8 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { // Process #ifdef PSUTIL_FREEBSD sprintf(str, "%s", kp.ki_comm); +#elif defined(PSUTIL_DRAGONFLY) + sprintf(str, "%s", kp.kp_comm); #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) sprintf(str, "%s", kp.p_comm); #endif @@ -223,6 +242,12 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { memtext = (long)kp.ki_tsize * pagesize; memdata = (long)kp.ki_dsize * pagesize; memstack = (long)kp.ki_ssize * pagesize; +#elif PSUTIL_DRAGONFLY + rss = (long)kp.kp_vm_rssize * pagesize; + vms = (long)kp.kp_vm_map_size; + memtext = (long)kp.kp_vm_tsize * pagesize; + memdata = (long)kp.kp_vm_dsize * pagesize; + memstack = (long)kp.kp_vm_ssize * pagesize; #else rss = (long)kp.p_vm_rssize * pagesize; #ifdef PSUTIL_OPENBSD @@ -250,6 +275,8 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { oncpu = kp.ki_oncpu; else oncpu = kp.ki_lastcpu; +#elif defined(PSUTIL_DRAGONFLY) + oncpu = kp.kp_lwp.kl_cpuid; #else // On Net/OpenBSD we have kp.p_cpuid but it appears it's always // set to KI_NOCPU. Even if it's not, ki_lastcpu does not exist @@ -260,6 +287,8 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { #ifdef PSUTIL_FREEBSD py_ppid = PyLong_FromPid(kp.ki_ppid); +#elif defined(PSUTIL_DRAGONFLY) + py_ppid = PyLong_FromPid(kp.kp_ppid); #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) py_ppid = PyLong_FromPid(kp.p_ppid); #else @@ -308,6 +337,39 @@ psutil_proc_oneshot_info(PyObject *self, PyObject *args) { memstack, // (long) mem stack // others oncpu, // (int) the CPU we are on +#elif defined(PSUTIL_DRAGONFLY) + py_ppid, // (pid_t) ppid + (int)kp.kp_stat, // (int) status + // UIDs + (long)kp.kp_ruid, // (long) real uid + (long)kp.kp_uid, // (long) effective uid + (long)kp.kp_svuid, // (long) saved uid + // GIDs + (long)kp.kp_rgid, // (long) real gid + (long)kp.kp_groups[0], // (long) effective gid + (long)kp.kp_svuid, // (long) saved gid + // + kp.kp_tdev, // (int or long long) tty nr + PSUTIL_TV2DOUBLE(kp.kp_start), // (double) create time + // ctx switches + kp.kp_ru.ru_nvcsw, // (long) ctx switches (voluntary) + kp.kp_ru.ru_nivcsw, // (long) ctx switches (unvoluntary) + // IO count + kp.kp_ru.ru_inblock, // (long) read io count + kp.kp_ru.ru_oublock, // (long) write io count + // CPU times: convert from micro seconds to seconds. + PSUTIL_TV2DOUBLE(kp.kp_ru.ru_utime), // (double) user time + PSUTIL_TV2DOUBLE(kp.kp_ru.ru_stime), // (double) sys time + PSUTIL_TV2DOUBLE(kp.kp_cru.ru_utime), // (double) children utime + PSUTIL_TV2DOUBLE(kp.kp_cru.ru_stime), // (double) children stime + // memory + rss, // (long) rss + vms, // (long) vms + memtext, // (long) mem text + memdata, // (long) mem data + memstack, // (long) mem stack + // others + oncpu, // (int) the CPU we are on #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) py_ppid, // (pid_t) ppid (int)kp.p_stat, // (int) status @@ -412,8 +474,10 @@ psutil_proc_environ(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "l", &pid)) return NULL; -#if defined(PSUTIL_FREEBSD) +#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_DRAGONFLY) kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); +#elif defined(PSUTIL_DRAGONFLY) + kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); #else kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); #endif @@ -458,6 +522,8 @@ psutil_proc_environ(PyObject *self, PyObject *args) { #endif #elif defined(PSUTIL_NETBSD) if ((p)->p_stat == SZOMB) { +#elif defined(PSUTIL_DRAGONFLY) + if ((p)->kp_flags & P_SYSTEM) { #elif defined(PSUTIL_OPENBSD) if ((p)->p_flag & P_SYSTEM) { #endif @@ -1072,14 +1138,16 @@ static PyMethodDef mod_methods[] = { {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS}, {"proc_open_files", psutil_proc_open_files, METH_VARARGS}, #endif -#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD) +#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD) || defined(PSUTIL_DRAGONFLY) {"proc_num_threads", psutil_proc_num_threads, METH_VARARGS}, #endif -#if defined(PSUTIL_FREEBSD) +#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_DRAGONFLY) {"cpu_topology", psutil_cpu_topology, METH_VARARGS}, {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS}, {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS}, {"proc_exe", psutil_proc_exe, METH_VARARGS}, +#endif +#if defined(PSUTIL_FREEBSD) {"proc_getrlimit", psutil_proc_getrlimit, METH_VARARGS}, {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS}, {"proc_setrlimit", psutil_proc_setrlimit, METH_VARARGS}, @@ -1102,12 +1170,15 @@ static PyMethodDef mod_methods[] = { #if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD) {"cpu_freq", psutil_cpu_freq, METH_VARARGS}, #endif -#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD) +#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD) || defined(PSUTIL_DRAGONFLY) {"net_connections", psutil_net_connections, METH_VARARGS}, #endif #if defined(PSUTIL_FREEBSD) {"sensors_battery", psutil_sensors_battery, METH_VARARGS}, {"sensors_cpu_temperature", psutil_sensors_cpu_temperature, METH_VARARGS}, +#endif +#ifdef PSUTIL_DRAGONFLY + {"cpu_count_cores", psutil_cpu_count_cores, METH_VARARGS}, #endif // --- others {"set_debug", psutil_set_debug, METH_VARARGS}, @@ -1157,6 +1228,12 @@ static PyMethodDef mod_methods[] = { if (PyModule_AddIntConstant(mod, "SZOMB", SZOMB)) INITERR; if (PyModule_AddIntConstant(mod, "SWAIT", SWAIT)) INITERR; if (PyModule_AddIntConstant(mod, "SLOCK", SLOCK)) INITERR; +#elif defined(PSUTIL_DRAGONFLY) + if (PyModule_AddIntConstant(mod, "SIDL", SIDL)) INITERR; + if (PyModule_AddIntConstant(mod, "SACTIVE", SACTIVE)) INITERR; + if (PyModule_AddIntConstant(mod, "SSTOP", SSTOP)) INITERR; + if (PyModule_AddIntConstant(mod, "SZOMB", SZOMB)) INITERR; + if (PyModule_AddIntConstant(mod, "SCORE", SCORE)) INITERR; #elif PSUTIL_OPENBSD if (PyModule_AddIntConstant(mod, "SIDL", SIDL)) INITERR; if (PyModule_AddIntConstant(mod, "SRUN", SRUN)) INITERR; diff --git psutil/_psutil_common.c psutil/_psutil_common.c index 096e2f37..eb588cd7 100644 --- psutil/_psutil_common.c +++ psutil/_psutil_common.c @@ -155,7 +155,8 @@ psutil_set_debug(PyObject *self, PyObject *args) { // Utility functions (BSD) // ============================================================================ -#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) +#if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) || \ + defined(PSUTIL_DRAGONFLY) void convert_kvm_err(const char *syscall, char *errbuf) { char fullmsg[8192]; diff --git psutil/arch/dragonfly/cpu.c psutil/arch/dragonfly/cpu.c new file mode 100644 index 00000000..581182a6 --- /dev/null +++ psutil/arch/dragonfly/cpu.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* +System-wide CPU related functions. +Original code was refactored and moved from psutil/arch/freebsd/specific.c +in 2020 (and was moved in there previously already) from cset. +a4c0a0eb0d2a872ab7a45e47fcf37ef1fde5b012 +For reference, here's the git history with original(ish) implementations: +- CPU stats: fb0154ef164d0e5942ac85102ab660b8d2938fbb +- CPU freq: 459556dd1e2979cdee22177339ced0761caf4c83 +- CPU cores: e0d6d7865df84dc9a1d123ae452fd311f79b1dde +*/ + + +#include +#include +#include +#include + +#include "../../_psutil_common.h" +#include "../../_psutil_posix.h" + + +/* + * Return the number of cores in the system. + */ +PyObject * +psutil_cpu_count_cores(PyObject *self, PyObject *args) { + int mib[2]; + int phys_cpus; + int cpu_cores; + size_t len; + + // physical cpus + if (sysctlbyname("hw.cpu_topology_phys_ids", &phys_cpus, &len, NULL, 0) == -1) { + PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('hw.cpu_topology_phys_ids')"); + Py_RETURN_NONE; // mimic os.cpu_count() + } + + // cores per package + if (sysctlbyname("hw.cpu_topology_core_ids", &cpu_cores, &len, NULL, 0) == -1) { + PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('hw.cpu_topology_core_ids')"); + Py_RETURN_NONE; // mimic os.cpu_count() + } + + return Py_BuildValue("i", phys_cpus * cpu_cores); +} + +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("hw.ncpu", &maxcpus, &size, NULL, 0) < 0) { + Py_DECREF(py_retlist); + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('hw.ncpu')"); + } + 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_SetFromOSErrnoWithSyscall("sysctl(HW_NCPU)"); + goto error; + } + + // per-cpu info + size = sizeof(cpu_time); + if (sysctlbyname("kern.cp_times", &cpu_time, &size, NULL, 0) == -1) { + PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('kern.cp_times')"); + 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; +} + + +PyObject * +psutil_cpu_topology(PyObject *self, PyObject *args) { + void *topology = NULL; + size_t size = 0; + PyObject *py_str; + + if (sysctlbyname("hw.cpu_topology.tree", NULL, &size, NULL, 0)) + goto error; + + topology = malloc(size); + if (!topology) { + PyErr_NoMemory(); + return NULL; + } + + if (sysctlbyname("hw.cpu_topology.tree", 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; +} + + +PyObject * +psutil_cpu_stats(PyObject *self, PyObject *args) { + unsigned int v_soft; + unsigned int v_intr; + unsigned int v_syscall; + unsigned int v_trap; + unsigned int v_swtch; + size_t size = sizeof(v_soft); + + if (sysctlbyname("vm.stats.sys.v_soft", &v_soft, &size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.sys.v_soft')"); + } + if (sysctlbyname("vm.stats.sys.v_intr", &v_intr, &size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.sys.v_intr')"); + } + if (sysctlbyname("vm.stats.sys.v_syscall", &v_syscall, &size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.sys.v_syscall')"); + } + if (sysctlbyname("vm.stats.sys.v_trap", &v_trap, &size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.sys.v_trap')"); + } + if (sysctlbyname("vm.stats.sys.v_swtch", &v_swtch, &size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.sys.v_swtch')"); + } + + return Py_BuildValue( + "IIIII", + v_swtch, // ctx switches + v_intr, // interrupts + v_soft, // software interrupts + v_syscall, // syscalls + v_trap // traps + ); +} diff --git psutil/arch/dragonfly/cpu.h psutil/arch/dragonfly/cpu.h new file mode 100644 index 00000000..a0de54f0 --- /dev/null +++ psutil/arch/dragonfly/cpu.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject *psutil_cpu_freq(PyObject* self, PyObject* args); +PyObject *psutil_cpu_stats(PyObject* self, PyObject* args); +PyObject *psutil_cpu_topology(PyObject* self, PyObject* args); +PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args); +PyObject *psutil_cpu_count_cores(PyObject *self, PyObject *args); diff --git psutil/arch/dragonfly/disk.c psutil/arch/dragonfly/disk.c new file mode 100644 index 00000000..2704fd73 --- /dev/null +++ psutil/arch/dragonfly/disk.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include +#include + +#include "../../_psutil_common.h" +#include "../../_psutil_posix.h" + + +// convert a bintime struct to milliseconds +#define PSUTIL_BT2MSEC(bt) (bt.sec * 1000 + (((uint64_t) 1000000000 * (uint32_t) \ + (bt.frac >> 32) ) >> 32 ) / 1000000) + +#define PSUTIL_TV2MSEC(tv) (tv.tv_sec * 1000 + (1000000L * tv.tv_usec)) + + +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 (checkversion() < 0) { + PyErr_Format(PyExc_RuntimeError, + "devstat_checkversion() syscall 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 (getdevs(&stats) == -1) { + PyErr_Format(PyExc_RuntimeError, "devstat_getdevs() syscall 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( + "(KKKKL)", + current.num_reads, // no reads + current.num_writes, // no writes + current.bytes_read, // bytes read + current.bytes_written, // bytes written + (long long) PSUTIL_TV2MSEC(current.busy_time) // busy 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; +} + diff --git psutil/arch/dragonfly/disk.h psutil/arch/dragonfly/disk.h new file mode 100644 index 00000000..9e29f664 --- /dev/null +++ psutil/arch/dragonfly/disk.h @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject *psutil_disk_io_counters(PyObject* self, PyObject* args); diff --git psutil/arch/dragonfly/mem.c psutil/arch/dragonfly/mem.c new file mode 100644 index 00000000..bda5159d --- /dev/null +++ psutil/arch/dragonfly/mem.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../../_psutil_common.h" +#include "../../_psutil_posix.h" + + +#ifndef _PATH_DEVNULL + #define _PATH_DEVNULL "/dev/null" +#endif + + +PyObject * +psutil_virtual_mem(PyObject *self, PyObject *args) { + unsigned long total; + unsigned int active, inactive, wired, cached, free; + size_t size = sizeof(total); + struct vmtotal vm; + int mib[] = {CTL_VM, VM_METER}; + long pagesize = psutil_getpagesize(); + long buffers; + size_t buffers_size = sizeof(buffers); + + if (sysctlbyname("hw.physmem", &total, &size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('hw.physmem')"); + } + if (sysctlbyname("vm.stats.vm.v_active_count", &active, &size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_active_count')"); + } + if (sysctlbyname("vm.stats.vm.v_inactive_count", &inactive, &size, NULL, 0)) + { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_inactive_count')"); + } + if (sysctlbyname("vm.stats.vm.v_wire_count", &wired, &size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_wire_count')"); + } + // https://github.com/giampaolo/psutil/issues/997 + if (sysctlbyname("vm.stats.vm.v_cache_count", &cached, &size, NULL, 0)) { + cached = 0; + } + if (sysctlbyname("vm.stats.vm.v_free_count", &free, &size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_free_count')"); + } + if (sysctlbyname("vfs.bufspace", &buffers, &buffers_size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('vfs.bufspace')"); + } + + size = sizeof(vm); + if (sysctlbyname("vm.vmtotal", &vm, &size, NULL, 0)) { + return PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('vm.vmtotal')"); + } + + return Py_BuildValue("KKKKKKKK", + (unsigned long long) total, + (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 + ); +} + + +PyObject * +psutil_swap_mem(PyObject *self, PyObject *args) { + // Return swap memory stats (see 'swapinfo' cmdline tool) + kvm_t *kd; + struct kvm_swap kvmsw[1]; + unsigned int swapin, swapout, nodein, nodeout; + size_t size = sizeof(unsigned int); + long pagesize = psutil_getpagesize(); + + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open failed"); + if (kd == NULL) { + PyErr_SetString(PyExc_RuntimeError, "kvm_open() syscall failed"); + return NULL; + } + + if (kvm_getswapinfo(kd, kvmsw, 1, 0) < 0) { + kvm_close(kd); + PyErr_SetString(PyExc_RuntimeError, + "kvm_getswapinfo() syscall failed"); + return NULL; + } + + kvm_close(kd); + + if (sysctlbyname("vm.stats.vm.v_swapin", &swapin, &size, NULL, 0) == -1) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_swapin)'"); + } + if (sysctlbyname("vm.stats.vm.v_swapout", &swapout, &size, NULL, 0) == -1){ + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_swapout)'"); + } + if (sysctlbyname("vm.stats.vm.v_vnodein", &nodein, &size, NULL, 0) == -1) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_vnodein)'"); + } + if (sysctlbyname("vm.stats.vm.v_vnodeout", &nodeout, &size, NULL, 0) == -1) { + return PyErr_SetFromOSErrnoWithSyscall( + "sysctlbyname('vm.stats.vm.v_vnodeout)'"); + } + + return Py_BuildValue( + "(KKKII)", + (unsigned long long)kvmsw[0].ksw_total * pagesize, // total + (unsigned long long)kvmsw[0].ksw_used * pagesize, // used + (unsigned long long)kvmsw[0].ksw_total * pagesize - // free + kvmsw[0].ksw_used * pagesize, + swapin + swapout, // swap in + nodein + nodeout // swap out + ); +} + diff --git psutil/arch/dragonfly/mem.h psutil/arch/dragonfly/mem.h new file mode 100644 index 00000000..e7dcfc57 --- /dev/null +++ psutil/arch/dragonfly/mem.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject *psutil_swap_mem(PyObject* self, PyObject* args); +PyObject *psutil_virtual_mem(PyObject* self, PyObject* args); diff --git psutil/arch/dragonfly/proc.c psutil/arch/dragonfly/proc.c new file mode 100644 index 00000000..d06eb65f --- /dev/null +++ psutil/arch/dragonfly/proc.c @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // process open files, shared libs (kinfo_getvmmap), cwd + +#include "../../_psutil_common.h" +#include "../../_psutil_posix.h" + +#define PSUTIL_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 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_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_PID)"); + return -1; + } + + // sysctl stores 0 in the size if we can't find the process information. + if (size == 0) { + NoSuchProcess("sysctl (size = 0)"); + return -1; + } + return 0; +} + + +// remove spaces from string +static void psutil_remove_spaces(char *str) { + char *p1 = str; + char *p2 = str; + do + while (*p2 == ' ') + p2++; + while ((*p1++ = *p2++)); +} + + +// ============================================================================ +// 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. On success returns 0, else 1 with exception set. + int err; + struct kinfo_proc *buf = NULL; + int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; + size_t length = 0; + + assert(procList != NULL); + assert(*procList == NULL); + assert(procCount != NULL); + + // Call sysctl with a NULL buffer in order to get buffer length. + err = sysctl(name, 3, NULL, &length, NULL, 0); + if (err == -1) { + PyErr_SetFromOSErrnoWithSyscall("sysctl (null buffer)"); + return 1; + } + + // Allocate an appropriately sized buffer based on the results + // from the previous call. + buf = malloc(length); + if (buf == NULL) { + PyErr_NoMemory(); + return 1; + } + + // Call sysctl again with the new buffer. + err = sysctl(name, 3, buf, &length, NULL, 0); + if (err == -1) { + PyErr_SetFromOSErrnoWithSyscall("sysctl"); + free(buf); + return 1; + } + + *procList = buf; + *procCount = length / sizeof(struct kinfo_proc); + return 0; +} + + +/* + * XXX no longer used; it probably makese sense to remove it. + * Borrowed from psi Python System Information project + * + * Get command arguments and environment variables. + * + * Based on code from ps. + * + * Returns: + * 0 for success; + * -1 for failure (Exception raised); + * 1 for insufficient privileges. + */ +static char +*psutil_get_cmd_args(pid_t pid, size_t *argsize) { + int mib[4]; + int argmax; + size_t size = sizeof(argmax); + char *procargs = NULL; + + // Get the maximum process arguments size. + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + size = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) + return NULL; + + // Allocate space for the arguments. + procargs = (char *)malloc(argmax); + if (procargs == NULL) { + PyErr_NoMemory(); + return NULL; + } + + // Make a sysctl() call to get the raw argument space of the process. + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_ARGS; + mib[3] = pid; + + size = argmax; + if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) { + free(procargs); + PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ARGS)"); + return NULL; + } + + // return string and set the length of arguments + *argsize = size; + return procargs; +} + + +// returns the command line as a python list object +PyObject * +psutil_get_cmdline(pid_t pid) { + char *argstr = NULL; + size_t pos = 0; + size_t argsize = 0; + PyObject *py_retlist = Py_BuildValue("[]"); + PyObject *py_arg = NULL; + + if (pid < 0) + return py_retlist; + argstr = psutil_get_cmd_args(pid, &argsize); + if (argstr == NULL) + goto error; + + // args are returned as a flattened string with \0 separators between + // arguments add each string to the list then step forward to the next + // separator + if (argsize > 0) { + while (pos < argsize) { + py_arg = PyUnicode_DecodeFSDefault(&argstr[pos]); + if (!py_arg) + goto error; + if (PyList_Append(py_retlist, py_arg)) + goto error; + Py_DECREF(py_arg); + pos = pos + strlen(&argstr[pos]) + 1; + } + } + + free(argstr); + return py_retlist; + +error: + Py_XDECREF(py_arg); + Py_DECREF(py_retlist); + if (argstr != NULL) + free(argstr); + return NULL; +} + + +/* + * Return process pathname executable. + * Thanks to Robert N. M. Watson: + * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT + */ +PyObject * +psutil_proc_exe(PyObject *self, PyObject *args) { + pid_t pid; + char pathname[PATH_MAX]; + int error; + int mib[4]; + int ret; + size_t size; + + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &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) { + // see: https://github.com/giampaolo/psutil/issues/907 + if (errno == ENOENT) { + return PyUnicode_DecodeFSDefault(""); + } + else { + return \ + PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_PATHNAME)"); + } + } + if (size == 0 || strlen(pathname) == 0) { + ret = psutil_pid_exists(pid); + if (ret == -1) + return NULL; + else if (ret == 0) + return NoSuchProcess("psutil_pid_exists -> 0"); + else + strcpy(pathname, ""); + } + + return PyUnicode_DecodeFSDefault(pathname); +} + + +PyObject * +psutil_proc_num_threads(PyObject *self, PyObject *args) { + // Return number of threads used by process as a Python integer. + pid_t pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) + return NULL; + if (psutil_kinfo_proc(pid, &kp) == -1) + return NULL; + return Py_BuildValue("l", (long)kp.kp_nthreads); +} + + +PyObject * +psutil_proc_threads(PyObject *self, PyObject *args) { + // 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: + // http://code.metager.de/source/xref/freebsd/usr.bin/procstat/ + // procstat_threads.c + pid_t 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, _Py_PARSE_PID, &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_FLAG_LWP; + mib[3] = pid; + + size = 0; + error = sysctl(mib, 4, NULL, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_INC_THREAD)"); + goto error; + } + if (size == 0) { + NoSuchProcess("sysctl (size = 0)"); + goto error; + } + + kip = malloc(size); + if (kip == NULL) { + PyErr_NoMemory(); + goto error; + } + + error = sysctl(mib, 4, kip, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_INC_THREAD)"); + goto error; + } + if (size == 0) { + NoSuchProcess("sysctl (size = 0)"); + goto error; + } + + for (i = 0; i < size / sizeof(*kipp); i++) { + kipp = &kip[i]; + py_tuple = Py_BuildValue("Idd", + kipp->kp_lwp.kl_tid, + PSUTIL_TV2DOUBLE(kipp->kp_lwp.kl_ru.ru_utime), + PSUTIL_TV2DOUBLE(kipp->kp_lwp.kl_ru.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; +} + +PyObject * +psutil_proc_cwd(PyObject *self, PyObject *args) { + size_t size; + pid_t pid; + int error; + char *cwd; + int mib[4]; + PyObject *py_path = NULL; + + int i, cnt; + + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) + goto out; + + // Allocate space for the arguments. + cwd = (char *)malloc(PATH_MAX); + if (cwd == NULL) { + PyErr_NoMemory(); + return NULL; + } + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_CWD; + mib[3] = pid; + error = sysctl(mib, 4, cwd, &size, NULL, 0); + if (error == -1) { + free(cwd); + PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_CWD)"); + return NULL; + } + + py_path = PyUnicode_DecodeFSDefault(cwd); + if (!py_path) + goto out; + + return py_path; + +out: + Py_XDECREF(py_path); + if (cwd != NULL) + free(cwd); + return NULL; +} + +PyObject* +psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args) { + // Get process CPU affinity. + // Reference: + // http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c + pid_t pid; + int ret; + int i; + cpu_set_t mask; + PyObject* py_retlist; + PyObject* py_cpu_num; + + CPU_ZERO(&mask); + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) + return NULL; + ret = sched_getaffinity(pid, sizeof(mask), &mask); + if (ret != 0) + return PyErr_SetFromErrno(PyExc_OSError); + + 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; +} + + +PyObject * +psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { + // Set process CPU affinity. + // Reference: + // http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c + pid_t pid; + int i; + int seq_len; + int ret; + cpu_set_t cpu_set; + PyObject *py_cpu_set; + PyObject *py_cpu_seq = NULL; + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &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 = sched_setaffinity(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; +} diff --git psutil/arch/dragonfly/proc.h psutil/arch/dragonfly/proc.h new file mode 100644 index 00000000..9c16f3cb --- /dev/null +++ psutil/arch/dragonfly/proc.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +typedef struct kinfo_proc kinfo_proc; + +int psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount); +int psutil_kinfo_proc(const pid_t pid, struct kinfo_proc *proc); + +PyObject* psutil_get_cmdline(long pid); +PyObject* psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args); +PyObject* psutil_proc_cpu_affinity_set(PyObject* self, PyObject* args); +PyObject* psutil_proc_cwd(PyObject* self, PyObject* args); +PyObject* psutil_proc_exe(PyObject* self, PyObject* args); +PyObject* psutil_proc_getrlimit(PyObject* self, PyObject* args); +PyObject* psutil_proc_memory_maps(PyObject* self, PyObject* args); +PyObject* psutil_proc_num_fds(PyObject* self, PyObject* args); +PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args); +PyObject* psutil_proc_setrlimit(PyObject* self, PyObject* args); +PyObject* psutil_proc_threads(PyObject* self, PyObject* args); diff --git psutil/arch/dragonfly/proc_socks.h psutil/arch/dragonfly/proc_socks.h new file mode 100644 index 00000000..a7996b10 --- /dev/null +++ psutil/arch/dragonfly/proc_socks.h @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject* psutil_proc_connections(PyObject* self, PyObject* args); diff --git psutil/arch/dragonfly/sensors.c psutil/arch/dragonfly/sensors.c new file mode 100644 index 00000000..722e0930 --- /dev/null +++ psutil/arch/dragonfly/sensors.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* +Original code was refactored and moved from psutil/arch/freebsd/specific.c +For reference, here's the git history with original(ish) implementations: +- sensors_battery(): 022cf0a05d34f4274269d4f8002ee95b9f3e32d2 +- sensors_cpu_temperature(): bb5d032be76980a9e110f03f1203bd35fa85a793 + (patch by Alex Manuskin) +*/ + + +#include +#include + +#include "../../_psutil_common.h" +#include "../../_psutil_posix.h" + + +#define DECIKELVIN_2_CELCIUS(t) (t - 2731) / 10 + + +PyObject * +psutil_sensors_battery(PyObject *self, PyObject *args) { + int percent; + int minsleft; + int power_plugged; + size_t size = sizeof(percent); + + if (sysctlbyname("hw.acpi.battery.life", &percent, &size, NULL, 0)) + goto error; + if (sysctlbyname("hw.acpi.battery.time", &minsleft, &size, NULL, 0)) + goto error; + if (sysctlbyname("hw.acpi.acline", &power_plugged, &size, NULL, 0)) + goto error; + return Py_BuildValue("iii", percent, minsleft, power_plugged); + +error: + // see: https://github.com/giampaolo/psutil/issues/1074 + if (errno == ENOENT) + PyErr_SetString(PyExc_NotImplementedError, "no battery"); + else + PyErr_SetFromErrno(PyExc_OSError); + return NULL; +} + + +// Return temperature information for a given CPU core number. +PyObject * +psutil_sensors_cpu_temperature(PyObject *self, PyObject *args) { + int current; + int tjmax; + int core; + char sensor[26]; + size_t size = sizeof(current); + + if (! PyArg_ParseTuple(args, "i", &core)) + return NULL; + sprintf(sensor, "hw.sensors.cpu%d.temp0", core); + if (sysctlbyname(sensor, ¤t, &size, NULL, 0)) + goto error; + current = DECIKELVIN_2_CELCIUS(current); + + // Return -273 in case of faliure. + sprintf(sensor, "dev.cpu.%d.coretemp.tjmax", core); + if (sysctlbyname(sensor, &tjmax, &size, NULL, 0)) + tjmax = 0; + tjmax = DECIKELVIN_2_CELCIUS(tjmax); + + return Py_BuildValue("ii", current, tjmax); + +error: + if (errno == ENOENT) + PyErr_SetString(PyExc_NotImplementedError, "no temperature sensors"); + else + PyErr_SetFromErrno(PyExc_OSError); + return NULL; +} + diff --git psutil/arch/dragonfly/sensors.h psutil/arch/dragonfly/sensors.h new file mode 100644 index 00000000..e5c4107b --- /dev/null +++ psutil/arch/dragonfly/sensors.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject* psutil_sensors_battery(PyObject* self, PyObject* args); +PyObject* psutil_sensors_cpu_temperature(PyObject* self, PyObject* args); diff --git psutil/arch/dragonfly/sys_socks.c psutil/arch/dragonfly/sys_socks.c new file mode 100644 index 00000000..f2ea848e --- /dev/null +++ psutil/arch/dragonfly/sys_socks.c @@ -0,0 +1,315 @@ +/* + * 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. + * + * Copyright (c) 2005 Joerg Sonnenberger . All rights reserved. + * Copyright (c) 2002 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + */ + +#include +#include +#include +#include // for struct xsocket +#include +#include +#include +#include // for xinpcb struct +#include +#include +#include // for struct xtcpcb +#include // for inet_ntop() +#include /* For kinfo_get_files */ + +#include "../../_psutil_common.h" +#include "../../_psutil_posix.h" + +static struct kinfo_file *psutil_xfiles; +static size_t psutil_nxfiles; + +#define HASHSIZE 1009 +static struct sock *sockhash[HASHSIZE]; + +int +psutil_populate_xfiles(void) { + if (kinfo_get_files(&psutil_xfiles, &psutil_nxfiles)) { + if (errno == ENOMEM) + PyErr_NoMemory(); + else if (errno != ENOMEM) + PyErr_SetFromErrno(0); + + return 0; + } + + return 1; /* Success */ +} + +struct kinfo_file * +psutil_get_file_from_sock(void *sock) { + struct kinfo_file *xf; + int n; + + for (xf = psutil_xfiles, n = 0; n < (int)psutil_nxfiles; ++n, ++xf) { + if (xf->f_data == sock) + return xf; + } + return NULL; +} + +int psutil_gather_inet(int proto, PyObject *py_retlist) +{ + void *so_begin, *so_end; + struct xinpcb *xip; + struct xtcpcb *xtp; + struct inpcb *inp; + struct xsocket *so; + struct sock *sock; + const char *varname, *protoname; + size_t len; + void *buf; + int type; + + PyObject *py_tuple = NULL; + PyObject *py_laddr = NULL; + PyObject *py_raddr = NULL; + + switch (proto) { + case IPPROTO_TCP: + varname = "net.inet.tcp.pcblist"; + protoname = "tcp"; + break; + case IPPROTO_UDP: + varname = "net.inet.udp.pcblist"; + protoname = "udp"; + break; + default: + abort(); + } + + buf = NULL; + len = 0; + + if (sysctlbyname(varname, NULL, &len, NULL, 0)) { + PyErr_SetFromErrno(0); + return 0; + } + if ((buf = malloc(len)) == NULL) { + PyErr_NoMemory(); + return 0; + } + if (sysctlbyname(varname, buf, &len, NULL, 0)) { + PyErr_SetFromErrno(0); + return 0; + } + + so_begin = buf; + so_end = (uint8_t *)buf + len; + + for (so_begin = buf, so_end = (uint8_t *)so_begin + len; + (uint8_t *)so_begin + sizeof(size_t) < (uint8_t *)so_end && + (uint8_t *)so_begin + *(size_t *)so_begin <= (uint8_t *)so_end; + so_begin = (uint8_t *)so_begin + *(size_t *)so_begin) { + struct kinfo_file *xf; + int lport, rport, status, family; + + switch (proto) { + case IPPROTO_TCP: + xtp = (struct xtcpcb *)so_begin; + if (xtp->xt_len != sizeof *xtp) { + PyErr_Format(PyExc_RuntimeError, + "struct xtcpcb size mismatch"); + goto error; + } + inp = &xtp->xt_inp; + so = &xtp->xt_socket; + break; + case IPPROTO_UDP: + xip = (struct xinpcb *)so_begin; + if (xip->xi_len != sizeof *xip) { + PyErr_Format(PyExc_RuntimeError, + "struct xtcpcb size mismatch"); + goto error; + } + inp = &xip->xi_inp; + so = &xip->xi_socket; + break; + default: + PyErr_Format(PyExc_RuntimeError, "invalid proto"); + goto error; + } + + char lip[200], rip[200]; + + xf = psutil_get_file_from_sock(so->xso_so); + if (xf == NULL) + continue; + + lport = ntohs(inp->inp_lport); + rport = ntohs(inp->inp_fport); + + if (INP_ISIPV4(inp)) { + 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_ISIPV6(inp)) { + 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( + "iiiNNi" _Py_PARSE_PID, + xf->f_fd, // fd + family, // family + type, // type + py_laddr, // laddr + py_raddr, // raddr + status, // status + xf->f_pid // pid + ); + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_tuple); + + } + +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) { + void *so_begin, *so_end; + struct xunpcb *xup; + const char *varname, *protoname; + size_t len; + void *buf; + struct sockaddr_un *sun; + char path[PATH_MAX]; + + PyObject *py_tuple = NULL; + PyObject *py_lpath = 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; + len = 0; + + if (sysctlbyname(varname, NULL, &len, NULL, 0)) { + PyErr_SetFromErrno(0); + return 0; + } + if ((buf = malloc(len)) == NULL) { + PyErr_NoMemory(); + return 0; + } + if (sysctlbyname(varname, buf, &len, NULL, 0)) { + PyErr_SetFromErrno(0); + return 0; + } + + for (so_begin = buf, so_end = (uint8_t *)buf + len; + (uint8_t *)so_begin + sizeof(size_t) < (uint8_t *)so_end && + (uint8_t *)so_begin + *(size_t *)so_begin <= (uint8_t *)so_end; + so_begin = (uint8_t *)so_begin + *(size_t *)so_begin) { + struct kinfo_file *xf; + + xup = so_begin; + if (xup->xu_len != sizeof *xup) { + PyErr_Format(PyExc_RuntimeError, + "struct xtcpcb size mismatch"); + goto error; + } + + xf = psutil_get_file_from_sock(xup->xu_socket.xso_so); + if (xf == NULL) + 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_lpath = PyUnicode_DecodeFSDefault(path); + if (! py_lpath) + goto error; + + py_tuple = Py_BuildValue("(iiiOsii)", + xf->f_fd, // fd + AF_UNIX, // family + proto, // type + py_lpath, // lpath + "", // rath + PSUTIL_CONN_NONE, // status + xf->f_pid); // pid + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_DECREF(py_lpath); + Py_DECREF(py_tuple); + } + + free(buf); + return 1; + +error: + Py_XDECREF(py_tuple); + Py_XDECREF(py_lpath); + free(buf); + return 0; +} + +PyObject* +psutil_net_connections(PyObject* self, PyObject* args) { + // Return system-wide open connections. + 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; +} diff --git psutil/arch/dragonfly/sys_socks.h psutil/arch/dragonfly/sys_socks.h new file mode 100644 index 00000000..75247926 --- /dev/null +++ psutil/arch/dragonfly/sys_socks.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola'. + * All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject* psutil_net_connections(PyObject* self, PyObject* args); diff --git psutil/tests/test_system.py psutil/tests/test_system.py index d6b7a21a..75532f87 100755 --- psutil/tests/test_system.py +++ psutil/tests/test_system.py @@ -22,6 +22,7 @@ import psutil from psutil import AIX from psutil import BSD from psutil import FREEBSD +from psutil import DRAGONFLY from psutil import LINUX from psutil import MACOS from psutil import NETBSD @@ -650,7 +651,7 @@ class TestDiskAPIs(PsutilTestCase): self.assertEqual(nt[1], nt.write_count) self.assertEqual(nt[2], nt.read_bytes) self.assertEqual(nt[3], nt.write_bytes) - if not (OPENBSD or NETBSD): + if not (OPENBSD or NETBSD or DRAGONFLY): self.assertEqual(nt[4], nt.read_time) self.assertEqual(nt[5], nt.write_time) if LINUX: diff --git setup.py setup.py index 0f6716b3..ca0ad0b6 100755 --- setup.py +++ setup.py @@ -40,6 +40,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 DRAGONFLY # NOQA from _common import LINUX # NOQA from _common import MACOS # NOQA from _common import NETBSD # NOQA @@ -242,6 +243,22 @@ elif NETBSD: define_macros=macros, libraries=["kvm"]) +elif DRAGONFLY: + macros.append(("PSUTIL_DRAGONFLY", 1)) + ext = Extension( + 'psutil._psutil_bsd', + sources=sources + [ + 'psutil/_psutil_bsd.c', + 'psutil/arch/dragonfly/cpu.c', + 'psutil/arch/dragonfly/mem.c', + 'psutil/arch/dragonfly/disk.c', + 'psutil/arch/dragonfly/sensors.c', + 'psutil/arch/dragonfly/proc.c', + 'psutil/arch/dragonfly/sys_socks.c' + ], + define_macros=macros, + libraries=["kvm", "kinfo", "devstat"]) + elif LINUX: def get_ethtool_macro(): # see: https://github.com/giampaolo/psutil/issues/659 @@ -385,6 +402,7 @@ def main(): 'Operating System :: POSIX :: BSD :: FreeBSD', 'Operating System :: POSIX :: BSD :: NetBSD', 'Operating System :: POSIX :: BSD :: OpenBSD', + 'Operating System :: POSIX :: BSD :: DragonFly BSD', 'Operating System :: POSIX :: BSD', 'Operating System :: POSIX :: Linux', 'Operating System :: POSIX :: SunOS/Solaris', @@ -431,8 +449,8 @@ def main(): elif MACOS: print(hilite("XCode (https://developer.apple.com/xcode/) " "is not installed"), color="red", file=sys.stderr) - elif FREEBSD: - if which('pkg'): + elif FREEBSD or DRAGONFLY: + if which('pkg'): # FreeBSD/DragonFly missdeps("pkg install gcc python%s" % py3) elif which('mport'): # MidnightBSD missdeps("mport install gcc python%s" % py3)