From eb8e318628eeddc3b237449c08226785e9e2d589 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 14 Apr 2023 00:19:55 +0200 Subject: [NetBSD] move files / refactoring of C files (#2232) --- MANIFEST.in | 10 +- psutil/_psutil_bsd.c | 5 +- psutil/arch/netbsd/cpu.c | 102 +++++++ psutil/arch/netbsd/cpu.h | 11 + psutil/arch/netbsd/disk.c | 75 +++++ psutil/arch/netbsd/disk.h | 10 + psutil/arch/netbsd/mem.c | 111 +++++++ psutil/arch/netbsd/mem.h | 11 + psutil/arch/netbsd/proc.c | 427 ++++++++++++++++++++++++++ psutil/arch/netbsd/proc.h | 24 ++ psutil/arch/netbsd/specific.c | 688 ------------------------------------------ psutil/arch/netbsd/specific.h | 29 -- setup.py | 5 +- 13 files changed, 787 insertions(+), 721 deletions(-) create mode 100644 psutil/arch/netbsd/cpu.c create mode 100644 psutil/arch/netbsd/cpu.h create mode 100644 psutil/arch/netbsd/disk.c create mode 100644 psutil/arch/netbsd/disk.h create mode 100644 psutil/arch/netbsd/mem.c create mode 100644 psutil/arch/netbsd/mem.h create mode 100644 psutil/arch/netbsd/proc.c create mode 100644 psutil/arch/netbsd/proc.h delete mode 100644 psutil/arch/netbsd/specific.c delete mode 100644 psutil/arch/netbsd/specific.h diff --git a/MANIFEST.in b/MANIFEST.in index 2653b691..a594328d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -61,10 +61,16 @@ include psutil/arch/freebsd/sensors.c include psutil/arch/freebsd/sensors.h include psutil/arch/freebsd/sys_socks.c include psutil/arch/freebsd/sys_socks.h +include psutil/arch/netbsd/cpu.c +include psutil/arch/netbsd/cpu.h +include psutil/arch/netbsd/disk.c +include psutil/arch/netbsd/disk.h +include psutil/arch/netbsd/mem.c +include psutil/arch/netbsd/mem.h +include psutil/arch/netbsd/proc.c +include psutil/arch/netbsd/proc.h include psutil/arch/netbsd/socks.c include psutil/arch/netbsd/socks.h -include psutil/arch/netbsd/specific.c -include psutil/arch/netbsd/specific.h include psutil/arch/openbsd/cpu.c include psutil/arch/openbsd/cpu.h include psutil/arch/openbsd/disk.c diff --git a/psutil/_psutil_bsd.c b/psutil/_psutil_bsd.c index b2afbb28..ff5fd72d 100644 --- a/psutil/_psutil_bsd.c +++ b/psutil/_psutil_bsd.c @@ -93,7 +93,10 @@ #undef _KERNEL #include // for CPUSTATES & CP_* #elif PSUTIL_NETBSD - #include "arch/netbsd/specific.h" + #include "arch/netbsd/cpu.h" + #include "arch/netbsd/disk.h" + #include "arch/netbsd/mem.h" + #include "arch/netbsd/proc.h" #include "arch/netbsd/socks.h" #include diff --git a/psutil/arch/netbsd/cpu.c b/psutil/arch/netbsd/cpu.c new file mode 100644 index 00000000..33eb906f --- /dev/null +++ b/psutil/arch/netbsd/cpu.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. + * All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include +#include + + +/* +CPU related functions. Original code was refactored and moved from +psutil/arch/netbsd/specific.c in 2023 (and was moved in there previously +already) from cset 84219ad. For reference, here's the git history with +original(ish) implementations: +- per CPU times: 312442ad2a5b5d0c608476c5ab3e267735c3bc59 (Jan 2016) +- CPU stats: a991494e4502e1235ebc62b5ba450287d0dedec0 (Jan 2016) +*/ + + +PyObject * +psutil_cpu_stats(PyObject *self, PyObject *args) { + size_t size; + struct uvmexp_sysctl uv; + int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2}; + + size = sizeof(uv); + if (sysctl(uvmexp_mib, 2, &uv, &size, NULL, 0) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + return Py_BuildValue( + "IIIIIII", + uv.swtch, // ctx switches + uv.intrs, // interrupts - XXX always 0, will be determined via /proc + uv.softs, // soft interrupts + uv.syscalls, // syscalls - XXX always 0 + uv.traps, // traps + uv.faults, // faults + uv.forks // forks + ); +} + + +PyObject * +psutil_per_cpu_times(PyObject *self, PyObject *args) { + int mib[3]; + int ncpu; + size_t len; + size_t size; + int i; + PyObject *py_cputime = NULL; + PyObject *py_retlist = PyList_New(0); + + if (py_retlist == NULL) + return NULL; + // retrieve the number of cpus + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + len = sizeof(ncpu); + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + uint64_t cpu_time[CPUSTATES]; + + for (i = 0; i < ncpu; i++) { + // per-cpu info + mib[0] = CTL_KERN; + mib[1] = KERN_CP_TIME; + mib[2] = i; + size = sizeof(cpu_time); + if (sysctl(mib, 3, &cpu_time, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + py_cputime = Py_BuildValue( + "(ddddd)", + (double)cpu_time[CP_USER] / CLOCKS_PER_SEC, + (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC, + (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC, + (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC, + (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC + ); + if (!py_cputime) + goto error; + if (PyList_Append(py_retlist, py_cputime)) + goto error; + Py_DECREF(py_cputime); + } + + return py_retlist; + +error: + Py_XDECREF(py_cputime); + Py_DECREF(py_retlist); + return NULL; +} diff --git a/psutil/arch/netbsd/cpu.h b/psutil/arch/netbsd/cpu.h new file mode 100644 index 00000000..6c861032 --- /dev/null +++ b/psutil/arch/netbsd/cpu.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. + * All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject *psutil_cpu_stats(PyObject *self, PyObject *args); +PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args); diff --git a/psutil/arch/netbsd/disk.c b/psutil/arch/netbsd/disk.c new file mode 100644 index 00000000..5481f65d --- /dev/null +++ b/psutil/arch/netbsd/disk.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. + * All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* +Disk related functions. Original code was refactored and moved from +psutil/arch/netbsd/specific.c in 2023 (and was moved in there previously +already) from cset 84219ad. For reference, here's the git history with +original(ish) implementations: +- disk IO counters: 312442ad2a5b5d0c608476c5ab3e267735c3bc59 (Jan 2016) +*/ + +#include +#include +#include + + +PyObject * +psutil_disk_io_counters(PyObject *self, PyObject *args) { + int i, dk_ndrive, mib[3]; + size_t len; + struct io_sysctl *stats = NULL; + PyObject *py_disk_info = NULL; + PyObject *py_retdict = PyDict_New(); + + if (py_retdict == NULL) + return NULL; + mib[0] = CTL_HW; + mib[1] = HW_IOSTATS; + mib[2] = sizeof(struct io_sysctl); + len = 0; + if (sysctl(mib, 3, NULL, &len, NULL, 0) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + dk_ndrive = (int)(len / sizeof(struct io_sysctl)); + + stats = malloc(len); + if (stats == NULL) { + PyErr_NoMemory(); + goto error; + } + if (sysctl(mib, 3, stats, &len, NULL, 0) < 0 ) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + for (i = 0; i < dk_ndrive; i++) { + py_disk_info = Py_BuildValue( + "(KKKK)", + stats[i].rxfer, + stats[i].wxfer, + stats[i].rbytes, + stats[i].wbytes + ); + if (!py_disk_info) + goto error; + if (PyDict_SetItemString(py_retdict, stats[i].name, py_disk_info)) + goto error; + Py_DECREF(py_disk_info); + } + + free(stats); + return py_retdict; + +error: + Py_XDECREF(py_disk_info); + Py_DECREF(py_retdict); + if (stats != NULL) + free(stats); + return NULL; +} diff --git a/psutil/arch/netbsd/disk.h b/psutil/arch/netbsd/disk.h new file mode 100644 index 00000000..77691c0d --- /dev/null +++ b/psutil/arch/netbsd/disk.h @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. + * All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject *psutil_disk_io_counters(PyObject *self, PyObject *args); diff --git a/psutil/arch/netbsd/mem.c b/psutil/arch/netbsd/mem.c new file mode 100644 index 00000000..b7f5ba23 --- /dev/null +++ b/psutil/arch/netbsd/mem.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. + * All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* +Memory related functions. Original code was refactored and moved from +psutil/arch/netbsd/specific.c in 2023 (and was moved in there previously +already) from cset 84219ad. For reference, here's the git history with +original(ish) implementations: +- virtual memory: 0749a69c01b374ca3e2180aaafc3c95e3b2d91b9 (Oct 2016) +- swap memory: 312442ad2a5b5d0c608476c5ab3e267735c3bc59 (Jan 2016) +*/ + +#include +#include +#include +#include + +#include "../../_psutil_common.h" +#include "../../_psutil_posix.h" + + +// Virtual memory stats, taken from: +// https://github.com/satterly/zabbix-stats/blob/master/src/libs/zbxsysinfo/ +// netbsd/memory.c +PyObject * +psutil_virtual_mem(PyObject *self, PyObject *args) { + size_t size; + struct uvmexp_sysctl uv; + int mib[] = {CTL_VM, VM_UVMEXP2}; + long pagesize = psutil_getpagesize(); + + size = sizeof(uv); + if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + return Py_BuildValue("KKKKKKKK", + (unsigned long long) uv.npages << uv.pageshift, // total + (unsigned long long) uv.free << uv.pageshift, // free + (unsigned long long) uv.active << uv.pageshift, // active + (unsigned long long) uv.inactive << uv.pageshift, // inactive + (unsigned long long) uv.wired << uv.pageshift, // wired + (unsigned long long) (uv.filepages + uv.execpages) * pagesize, // cached + // These are determined from /proc/meminfo in Python. + (unsigned long long) 0, // buffers + (unsigned long long) 0 // shared + ); +} + + +PyObject * +psutil_swap_mem(PyObject *self, PyObject *args) { + uint64_t swap_total, swap_free; + struct swapent *swdev; + int nswap, i; + long pagesize = psutil_getpagesize(); + + nswap = swapctl(SWAP_NSWAP, 0, 0); + if (nswap == 0) { + // This means there's no swap partition. + return Py_BuildValue("(iiiii)", 0, 0, 0, 0, 0); + } + + swdev = calloc(nswap, sizeof(*swdev)); + if (swdev == NULL) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + if (swapctl(SWAP_STATS, swdev, nswap) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + // Total things up. + swap_total = swap_free = 0; + for (i = 0; i < nswap; i++) { + if (swdev[i].se_flags & SWF_ENABLE) { + swap_total += (uint64_t)swdev[i].se_nblks * DEV_BSIZE; + swap_free += (uint64_t)(swdev[i].se_nblks - swdev[i].se_inuse) * DEV_BSIZE; + } + } + free(swdev); + + // Get swap in/out + unsigned int total; + size_t size = sizeof(total); + struct uvmexp_sysctl uv; + int mib[] = {CTL_VM, VM_UVMEXP2}; + size = sizeof(uv); + if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + return Py_BuildValue("(LLLll)", + swap_total, + (swap_total - swap_free), + swap_free, + (long) uv.pgswapin * pagesize, // swap in + (long) uv.pgswapout * pagesize); // swap out + +error: + free(swdev); + return NULL; +} diff --git a/psutil/arch/netbsd/mem.h b/psutil/arch/netbsd/mem.h new file mode 100644 index 00000000..1de3f3c4 --- /dev/null +++ b/psutil/arch/netbsd/mem.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. + * All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject *psutil_virtual_mem(PyObject *self, PyObject *args); +PyObject *psutil_swap_mem(PyObject *self, PyObject *args); diff --git a/psutil/arch/netbsd/proc.c b/psutil/arch/netbsd/proc.c new file mode 100644 index 00000000..b87473a6 --- /dev/null +++ b/psutil/arch/netbsd/proc.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. + * All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Platform-specific module methods for NetBSD. + */ + +#include +#include +#include + +#include "../../_psutil_common.h" +#include "../../_psutil_posix.h" +#include "proc.h" + + +#define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0) +#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) + + +// ============================================================================ +// Utility functions +// ============================================================================ + + +int +psutil_kinfo_proc(pid_t pid, kinfo_proc *proc) { + // Fills a kinfo_proc struct based on process pid. + int ret; + int mib[6]; + size_t size = sizeof(kinfo_proc); + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC2; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + mib[4] = size; + mib[5] = 1; + + ret = sysctl((int*)mib, 6, proc, &size, NULL, 0); + if (ret == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + // sysctl stores 0 in the size if we can't find the process information. + if (size == 0) { + NoSuchProcess("sysctl (size = 0)"); + return -1; + } + return 0; +} + + +struct kinfo_file * +kinfo_getfile(pid_t pid, int* cnt) { + // Mimic's FreeBSD kinfo_file call, taking a pid and a ptr to an + // int as arg and returns an array with cnt struct kinfo_file. + int mib[6]; + size_t len; + struct kinfo_file* kf; + mib[0] = CTL_KERN; + mib[1] = KERN_FILE2; + mib[2] = KERN_FILE_BYPID; + mib[3] = (int) pid; + mib[4] = sizeof(struct kinfo_file); + mib[5] = 0; + + // get the size of what would be returned + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if ((kf = malloc(len)) == NULL) { + PyErr_NoMemory(); + return NULL; + } + mib[5] = (int)(len / sizeof(struct kinfo_file)); + if (sysctl(mib, 6, kf, &len, NULL, 0) < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + *cnt = (int)(len / sizeof(struct kinfo_file)); + return kf; +} + +PyObject * +psutil_proc_cwd(PyObject *self, PyObject *args) { + long pid; + + char path[MAXPATHLEN]; + size_t pathlen = sizeof path; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + +#ifdef KERN_PROC_CWD + int name[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_CWD}; + if (sysctl(name, 4, path, &pathlen, NULL, 0) != 0) { + if (errno == ENOENT) + NoSuchProcess("sysctl -> ENOENT"); + else + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } +#else + char *buf; + if (asprintf(&buf, "/proc/%d/cwd", (int)pid) < 0) { + PyErr_NoMemory(); + return NULL; + } + + ssize_t len = readlink(buf, path, sizeof(path) - 1); + free(buf); + if (len == -1) { + if (errno == ENOENT) + NoSuchProcess("readlink -> ENOENT"); + else + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + path[len] = '\0'; +#endif + + return PyUnicode_DecodeFSDefault(path); +} + + +// XXX: This is no longer used as per +// https://github.com/giampaolo/psutil/pull/557#issuecomment-171912820 +// Current implementation uses /proc instead. +// Left here just in case. +/* +PyObject * +psutil_proc_exe(PyObject *self, PyObject *args) { +#if __NetBSD_Version__ >= 799000000 + pid_t pid; + char pathname[MAXPATHLEN]; + int error; + int mib[4]; + int ret; + size_t size; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if (pid == 0) { + // else returns ENOENT + return Py_BuildValue("s", ""); + } + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC_ARGS; + mib[2] = pid; + mib[3] = KERN_PROC_PATHNAME; + + size = sizeof(pathname); + error = sysctl(mib, 4, NULL, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + error = sysctl(mib, 4, pathname, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if (size == 0 || strlen(pathname) == 0) { + ret = psutil_pid_exists(pid); + if (ret == -1) + return NULL; + else if (ret == 0) + return NoSuchProcess("psutil_pid_exists -> 0"); + else + strcpy(pathname, ""); + } + + return PyUnicode_DecodeFSDefault(pathname); +#else + return Py_BuildValue("s", ""); +#endif +} +*/ + +PyObject * +psutil_proc_num_threads(PyObject *self, PyObject *args) { + // Return number of threads used by process as a Python integer. + long pid; + kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if (psutil_kinfo_proc(pid, &kp) == -1) + return NULL; + return Py_BuildValue("l", (long)kp.p_nlwps); +} + +PyObject * +psutil_proc_threads(PyObject *self, PyObject *args) { + pid_t pid; + int mib[5]; + int i, nlwps; + ssize_t st; + size_t size; + struct kinfo_lwp *kl = NULL; + PyObject *py_retlist = PyList_New(0); + PyObject *py_tuple = NULL; + + if (py_retlist == NULL) + return NULL; + if (! PyArg_ParseTuple(args, "l", &pid)) + goto error; + + mib[0] = CTL_KERN; + mib[1] = KERN_LWP; + mib[2] = pid; + mib[3] = sizeof(struct kinfo_lwp); + mib[4] = 0; + + st = sysctl(mib, 5, NULL, &size, NULL, 0); + if (st == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (size == 0) { + NoSuchProcess("sysctl (size = 0)"); + goto error; + } + + mib[4] = size / sizeof(size_t); + kl = malloc(size); + if (kl == NULL) { + PyErr_NoMemory(); + goto error; + } + + st = sysctl(mib, 5, kl, &size, NULL, 0); + if (st == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (size == 0) { + NoSuchProcess("sysctl (size = 0)"); + goto error; + } + + nlwps = (int)(size / sizeof(struct kinfo_lwp)); + for (i = 0; i < nlwps; i++) { + py_tuple = Py_BuildValue("idd", + (&kl[i])->l_lid, + PSUTIL_KPT2DOUBLE((&kl[i])->l_rtime), + PSUTIL_KPT2DOUBLE((&kl[i])->l_rtime)); + if (py_tuple == NULL) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_DECREF(py_tuple); + } + free(kl); + return py_retlist; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + if (kl != NULL) + free(kl); + return NULL; +} + + +int +psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) { + // Returns a list of all BSD processes on the system. This routine + // allocates the list and puts it in *procList and a count of the + // number of entries in *procCount. You are responsible for freeing + // this list (use "free" from System framework). + // On success, the function returns 0. + // On error, the function returns a BSD errno value. + kinfo_proc *result; + // Declaring name as const requires us to cast it when passing it to + // sysctl because the prototype doesn't include the const modifier. + char errbuf[_POSIX2_LINE_MAX]; + int cnt; + kvm_t *kd; + + assert( procList != NULL); + assert(*procList == NULL); + assert(procCount != NULL); + + kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + + if (kd == NULL) { + PyErr_Format( + PyExc_RuntimeError, "kvm_openfiles() syscall failed: %s", errbuf); + return 1; + } + + result = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(kinfo_proc), &cnt); + if (result == NULL) { + PyErr_Format(PyExc_RuntimeError, "kvm_getproc2() syscall failed"); + kvm_close(kd); + return 1; + } + + *procCount = (size_t)cnt; + + size_t mlen = cnt * sizeof(kinfo_proc); + + if ((*procList = malloc(mlen)) == NULL) { + PyErr_NoMemory(); + kvm_close(kd); + return 1; + } + + memcpy(*procList, result, mlen); + assert(*procList != NULL); + kvm_close(kd); + + return 0; +} + + +char * +psutil_get_cmd_args(pid_t pid, size_t *argsize) { + int mib[4]; + int st; + size_t len; + char *procargs; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC_ARGS; + mib[2] = pid; + mib[3] = KERN_PROC_ARGV; + len = 0; + + st = sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0); + if (st == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + procargs = (char *)malloc(len); + if (procargs == NULL) { + PyErr_NoMemory(); + return NULL; + } + st = sysctl(mib, __arraycount(mib), procargs, &len, NULL, 0); + if (st == -1) { + free(procargs); + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + *argsize = len; + return procargs; +} + + +// Return the command line as a python list object. +// XXX - most of the times sysctl() returns a truncated string. +// Also /proc/pid/cmdline behaves the same so it looks like this +// is a kernel bug. +PyObject * +psutil_get_cmdline(pid_t pid) { + char *argstr = NULL; + size_t pos = 0; + size_t argsize = 0; + PyObject *py_arg = NULL; + PyObject *py_retlist = PyList_New(0); + + if (py_retlist == NULL) + return 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; +} + + +PyObject * +psutil_proc_num_fds(PyObject *self, PyObject *args) { + long pid; + int cnt; + + struct kinfo_file *freep; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + + errno = 0; + freep = kinfo_getfile(pid, &cnt); + if (freep == NULL) { + psutil_raise_for_pid(pid, "kinfo_getfile()"); + return NULL; + } + free(freep); + + return Py_BuildValue("i", cnt); +} diff --git a/psutil/arch/netbsd/proc.h b/psutil/arch/netbsd/proc.h new file mode 100644 index 00000000..b4c88851 --- /dev/null +++ b/psutil/arch/netbsd/proc.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. + * All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +typedef struct kinfo_proc2 kinfo_proc; + +int psutil_kinfo_proc(pid_t pid, kinfo_proc *proc); +struct kinfo_file * kinfo_getfile(pid_t pid, int* cnt); +int psutil_get_proc_list(kinfo_proc **procList, size_t *procCount); +char *psutil_get_cmd_args(pid_t pid, size_t *argsize); + +// +PyObject *psutil_get_cmdline(pid_t pid); +PyObject *psutil_proc_threads(PyObject *self, PyObject *args); +PyObject *psutil_proc_num_fds(PyObject *self, PyObject *args); +PyObject *psutil_proc_connections(PyObject *self, PyObject *args); +PyObject* psutil_proc_exe(PyObject* self, PyObject* args); +PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args); +PyObject *psutil_proc_cwd(PyObject *self, PyObject *args); diff --git a/psutil/arch/netbsd/specific.c b/psutil/arch/netbsd/specific.c deleted file mode 100644 index 11b8c1dd..00000000 --- a/psutil/arch/netbsd/specific.c +++ /dev/null @@ -1,688 +0,0 @@ -/* - * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. - * All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * Platform-specific module methods for NetBSD. - */ - -#if defined(PSUTIL_NETBSD) - #define _KMEMUSER -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // for swap_mem -#include -#include -// connection stuff -#include // for NI_MAXHOST -#include -#include // for CPUSTATES & CP_* -#define _KERNEL // for DTYPE_* - #include -#undef _KERNEL -#include // struct diskstats -#include -#include - -#include "../../_psutil_common.h" -#include "../../_psutil_posix.h" -#include "specific.h" - - -#define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0) -#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) - - -// ============================================================================ -// Utility functions -// ============================================================================ - - -int -psutil_kinfo_proc(pid_t pid, kinfo_proc *proc) { - // Fills a kinfo_proc struct based on process pid. - int ret; - int mib[6]; - size_t size = sizeof(kinfo_proc); - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC2; - mib[2] = KERN_PROC_PID; - mib[3] = pid; - mib[4] = size; - mib[5] = 1; - - ret = sysctl((int*)mib, 6, proc, &size, NULL, 0); - if (ret == -1) { - PyErr_SetFromErrno(PyExc_OSError); - return -1; - } - // sysctl stores 0 in the size if we can't find the process information. - if (size == 0) { - NoSuchProcess("sysctl (size = 0)"); - return -1; - } - return 0; -} - - -struct kinfo_file * -kinfo_getfile(pid_t pid, int* cnt) { - // Mimic's FreeBSD kinfo_file call, taking a pid and a ptr to an - // int as arg and returns an array with cnt struct kinfo_file. - int mib[6]; - size_t len; - struct kinfo_file* kf; - mib[0] = CTL_KERN; - mib[1] = KERN_FILE2; - mib[2] = KERN_FILE_BYPID; - mib[3] = (int) pid; - mib[4] = sizeof(struct kinfo_file); - mib[5] = 0; - - // get the size of what would be returned - if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - if ((kf = malloc(len)) == NULL) { - PyErr_NoMemory(); - return NULL; - } - mib[5] = (int)(len / sizeof(struct kinfo_file)); - if (sysctl(mib, 6, kf, &len, NULL, 0) < 0) { - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - - *cnt = (int)(len / sizeof(struct kinfo_file)); - return kf; -} - -PyObject * -psutil_proc_cwd(PyObject *self, PyObject *args) { - long pid; - - char path[MAXPATHLEN]; - size_t pathlen = sizeof path; - - if (! PyArg_ParseTuple(args, "l", &pid)) - return NULL; - -#ifdef KERN_PROC_CWD - int name[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_CWD}; - if (sysctl(name, 4, path, &pathlen, NULL, 0) != 0) { - if (errno == ENOENT) - NoSuchProcess("sysctl -> ENOENT"); - else - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } -#else - char *buf; - if (asprintf(&buf, "/proc/%d/cwd", (int)pid) < 0) { - PyErr_NoMemory(); - return NULL; - } - - ssize_t len = readlink(buf, path, sizeof(path) - 1); - free(buf); - if (len == -1) { - if (errno == ENOENT) - NoSuchProcess("readlink -> ENOENT"); - else - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - path[len] = '\0'; -#endif - - return PyUnicode_DecodeFSDefault(path); -} - - -// XXX: This is no longer used as per -// https://github.com/giampaolo/psutil/pull/557#issuecomment-171912820 -// Current implementation uses /proc instead. -// Left here just in case. -/* -PyObject * -psutil_proc_exe(PyObject *self, PyObject *args) { -#if __NetBSD_Version__ >= 799000000 - pid_t pid; - char pathname[MAXPATHLEN]; - int error; - int mib[4]; - int ret; - size_t size; - - if (! PyArg_ParseTuple(args, "l", &pid)) - return NULL; - if (pid == 0) { - // else returns ENOENT - return Py_BuildValue("s", ""); - } - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC_ARGS; - mib[2] = pid; - mib[3] = KERN_PROC_PATHNAME; - - size = sizeof(pathname); - error = sysctl(mib, 4, NULL, &size, NULL, 0); - if (error == -1) { - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - - error = sysctl(mib, 4, pathname, &size, NULL, 0); - if (error == -1) { - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - if (size == 0 || strlen(pathname) == 0) { - ret = psutil_pid_exists(pid); - if (ret == -1) - return NULL; - else if (ret == 0) - return NoSuchProcess("psutil_pid_exists -> 0"); - else - strcpy(pathname, ""); - } - - return PyUnicode_DecodeFSDefault(pathname); -#else - return Py_BuildValue("s", ""); -#endif -} -*/ - -PyObject * -psutil_proc_num_threads(PyObject *self, PyObject *args) { - // Return number of threads used by process as a Python integer. - long pid; - kinfo_proc kp; - if (! PyArg_ParseTuple(args, "l", &pid)) - return NULL; - if (psutil_kinfo_proc(pid, &kp) == -1) - return NULL; - return Py_BuildValue("l", (long)kp.p_nlwps); -} - -PyObject * -psutil_proc_threads(PyObject *self, PyObject *args) { - pid_t pid; - int mib[5]; - int i, nlwps; - ssize_t st; - size_t size; - struct kinfo_lwp *kl = NULL; - PyObject *py_retlist = PyList_New(0); - PyObject *py_tuple = NULL; - - if (py_retlist == NULL) - return NULL; - if (! PyArg_ParseTuple(args, "l", &pid)) - goto error; - - mib[0] = CTL_KERN; - mib[1] = KERN_LWP; - mib[2] = pid; - mib[3] = sizeof(struct kinfo_lwp); - mib[4] = 0; - - st = sysctl(mib, 5, NULL, &size, NULL, 0); - if (st == -1) { - PyErr_SetFromErrno(PyExc_OSError); - goto error; - } - if (size == 0) { - NoSuchProcess("sysctl (size = 0)"); - goto error; - } - - mib[4] = size / sizeof(size_t); - kl = malloc(size); - if (kl == NULL) { - PyErr_NoMemory(); - goto error; - } - - st = sysctl(mib, 5, kl, &size, NULL, 0); - if (st == -1) { - PyErr_SetFromErrno(PyExc_OSError); - goto error; - } - if (size == 0) { - NoSuchProcess("sysctl (size = 0)"); - goto error; - } - - nlwps = (int)(size / sizeof(struct kinfo_lwp)); - for (i = 0; i < nlwps; i++) { - py_tuple = Py_BuildValue("idd", - (&kl[i])->l_lid, - PSUTIL_KPT2DOUBLE((&kl[i])->l_rtime), - PSUTIL_KPT2DOUBLE((&kl[i])->l_rtime)); - if (py_tuple == NULL) - goto error; - if (PyList_Append(py_retlist, py_tuple)) - goto error; - Py_DECREF(py_tuple); - } - free(kl); - return py_retlist; - -error: - Py_XDECREF(py_tuple); - Py_DECREF(py_retlist); - if (kl != NULL) - free(kl); - return NULL; -} - - -// ============================================================================ -// APIS -// ============================================================================ - -int -psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) { - // Returns a list of all BSD processes on the system. This routine - // allocates the list and puts it in *procList and a count of the - // number of entries in *procCount. You are responsible for freeing - // this list (use "free" from System framework). - // On success, the function returns 0. - // On error, the function returns a BSD errno value. - kinfo_proc *result; - // Declaring name as const requires us to cast it when passing it to - // sysctl because the prototype doesn't include the const modifier. - char errbuf[_POSIX2_LINE_MAX]; - int cnt; - kvm_t *kd; - - assert( procList != NULL); - assert(*procList == NULL); - assert(procCount != NULL); - - kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); - - if (kd == NULL) { - PyErr_Format( - PyExc_RuntimeError, "kvm_openfiles() syscall failed: %s", errbuf); - return 1; - } - - result = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(kinfo_proc), &cnt); - if (result == NULL) { - PyErr_Format(PyExc_RuntimeError, "kvm_getproc2() syscall failed"); - kvm_close(kd); - return 1; - } - - *procCount = (size_t)cnt; - - size_t mlen = cnt * sizeof(kinfo_proc); - - if ((*procList = malloc(mlen)) == NULL) { - PyErr_NoMemory(); - kvm_close(kd); - return 1; - } - - memcpy(*procList, result, mlen); - assert(*procList != NULL); - kvm_close(kd); - - return 0; -} - - -char * -psutil_get_cmd_args(pid_t pid, size_t *argsize) { - int mib[4]; - int st; - size_t len; - char *procargs; - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC_ARGS; - mib[2] = pid; - mib[3] = KERN_PROC_ARGV; - len = 0; - - st = sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0); - if (st == -1) { - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - - procargs = (char *)malloc(len); - if (procargs == NULL) { - PyErr_NoMemory(); - return NULL; - } - st = sysctl(mib, __arraycount(mib), procargs, &len, NULL, 0); - if (st == -1) { - free(procargs); - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - - *argsize = len; - return procargs; -} - - -// Return the command line as a python list object. -// XXX - most of the times sysctl() returns a truncated string. -// Also /proc/pid/cmdline behaves the same so it looks like this -// is a kernel bug. -PyObject * -psutil_get_cmdline(pid_t pid) { - char *argstr = NULL; - size_t pos = 0; - size_t argsize = 0; - PyObject *py_arg = NULL; - PyObject *py_retlist = PyList_New(0); - - if (py_retlist == NULL) - return 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; -} - - -/* - * Virtual memory stats, taken from: - * https://github.com/satterly/zabbix-stats/blob/master/src/libs/zbxsysinfo/ - * netbsd/memory.c - */ -PyObject * -psutil_virtual_mem(PyObject *self, PyObject *args) { - size_t size; - struct uvmexp_sysctl uv; - int mib[] = {CTL_VM, VM_UVMEXP2}; - long pagesize = psutil_getpagesize(); - - size = sizeof(uv); - if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) { - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - - return Py_BuildValue("KKKKKKKK", - (unsigned long long) uv.npages << uv.pageshift, // total - (unsigned long long) uv.free << uv.pageshift, // free - (unsigned long long) uv.active << uv.pageshift, // active - (unsigned long long) uv.inactive << uv.pageshift, // inactive - (unsigned long long) uv.wired << uv.pageshift, // wired - (unsigned long long) (uv.filepages + uv.execpages) * pagesize, // cached - // These are determined from /proc/meminfo in Python. - (unsigned long long) 0, // buffers - (unsigned long long) 0 // shared - ); -} - - -PyObject * -psutil_swap_mem(PyObject *self, PyObject *args) { - uint64_t swap_total, swap_free; - struct swapent *swdev; - int nswap, i; - long pagesize = psutil_getpagesize(); - - nswap = swapctl(SWAP_NSWAP, 0, 0); - if (nswap == 0) { - // This means there's no swap partition. - return Py_BuildValue("(iiiii)", 0, 0, 0, 0, 0); - } - - swdev = calloc(nswap, sizeof(*swdev)); - if (swdev == NULL) { - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - - if (swapctl(SWAP_STATS, swdev, nswap) == -1) { - PyErr_SetFromErrno(PyExc_OSError); - goto error; - } - - // Total things up. - swap_total = swap_free = 0; - for (i = 0; i < nswap; i++) { - if (swdev[i].se_flags & SWF_ENABLE) { - swap_total += (uint64_t)swdev[i].se_nblks * DEV_BSIZE; - swap_free += (uint64_t)(swdev[i].se_nblks - swdev[i].se_inuse) * DEV_BSIZE; - } - } - free(swdev); - - // Get swap in/out - unsigned int total; - size_t size = sizeof(total); - struct uvmexp_sysctl uv; - int mib[] = {CTL_VM, VM_UVMEXP2}; - size = sizeof(uv); - if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) { - PyErr_SetFromErrno(PyExc_OSError); - goto error; - } - - return Py_BuildValue("(LLLll)", - swap_total, - (swap_total - swap_free), - swap_free, - (long) uv.pgswapin * pagesize, // swap in - (long) uv.pgswapout * pagesize); // swap out - -error: - free(swdev); - return NULL; -} - - -PyObject * -psutil_proc_num_fds(PyObject *self, PyObject *args) { - long pid; - int cnt; - - struct kinfo_file *freep; - - if (! PyArg_ParseTuple(args, "l", &pid)) - return NULL; - - errno = 0; - freep = kinfo_getfile(pid, &cnt); - if (freep == NULL) { - psutil_raise_for_pid(pid, "kinfo_getfile()"); - return NULL; - } - free(freep); - - return Py_BuildValue("i", cnt); -} - - -PyObject * -psutil_per_cpu_times(PyObject *self, PyObject *args) { - // XXX: why static? - int mib[3]; - int ncpu; - size_t len; - size_t size; - int i; - PyObject *py_cputime = NULL; - PyObject *py_retlist = PyList_New(0); - - if (py_retlist == NULL) - return NULL; - // retrieve the number of cpus - mib[0] = CTL_HW; - mib[1] = HW_NCPU; - len = sizeof(ncpu); - if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) { - PyErr_SetFromErrno(PyExc_OSError); - goto error; - } - uint64_t cpu_time[CPUSTATES]; - - for (i = 0; i < ncpu; i++) { - // per-cpu info - mib[0] = CTL_KERN; - mib[1] = KERN_CP_TIME; - mib[2] = i; - size = sizeof(cpu_time); - if (sysctl(mib, 3, &cpu_time, &size, NULL, 0) == -1) { - warn("failed to get kern.cptime2"); - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - - py_cputime = Py_BuildValue( - "(ddddd)", - (double)cpu_time[CP_USER] / CLOCKS_PER_SEC, - (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC, - (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC, - (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC, - (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC); - if (!py_cputime) - goto error; - if (PyList_Append(py_retlist, py_cputime)) - goto error; - Py_DECREF(py_cputime); - } - - return py_retlist; - -error: - Py_XDECREF(py_cputime); - Py_DECREF(py_retlist); - return NULL; -} - - -PyObject * -psutil_disk_io_counters(PyObject *self, PyObject *args) { - int i, dk_ndrive, mib[3]; - size_t len; - struct io_sysctl *stats = NULL; - PyObject *py_disk_info = NULL; - PyObject *py_retdict = PyDict_New(); - - if (py_retdict == NULL) - return NULL; - mib[0] = CTL_HW; - mib[1] = HW_IOSTATS; - mib[2] = sizeof(struct io_sysctl); - len = 0; - if (sysctl(mib, 3, NULL, &len, NULL, 0) < 0) { - warn("can't get HW_IOSTATS"); - PyErr_SetFromErrno(PyExc_OSError); - goto error; - } - dk_ndrive = (int)(len / sizeof(struct io_sysctl)); - - stats = malloc(len); - if (stats == NULL) { - PyErr_NoMemory(); - goto error; - } - if (sysctl(mib, 3, stats, &len, NULL, 0) < 0 ) { - PyErr_SetFromErrno(PyExc_OSError); - goto error; - } - - for (i = 0; i < dk_ndrive; i++) { - py_disk_info = Py_BuildValue( - "(KKKK)", - stats[i].rxfer, - stats[i].wxfer, - stats[i].rbytes, - stats[i].wbytes - ); - if (!py_disk_info) - goto error; - if (PyDict_SetItemString(py_retdict, stats[i].name, py_disk_info)) - goto error; - Py_DECREF(py_disk_info); - } - - free(stats); - return py_retdict; - -error: - Py_XDECREF(py_disk_info); - Py_DECREF(py_retdict); - if (stats != NULL) - free(stats); - return NULL; -} - - -PyObject * -psutil_cpu_stats(PyObject *self, PyObject *args) { - size_t size; - struct uvmexp_sysctl uv; - int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2}; - - size = sizeof(uv); - if (sysctl(uvmexp_mib, 2, &uv, &size, NULL, 0) < 0) { - PyErr_SetFromErrno(PyExc_OSError); - return NULL; - } - - return Py_BuildValue( - "IIIIIII", - uv.swtch, // ctx switches - uv.intrs, // interrupts - XXX always 0, will be determined via /proc - uv.softs, // soft interrupts - uv.syscalls, // syscalls - XXX always 0 - uv.traps, // traps - uv.faults, // faults - uv.forks // forks - ); -} diff --git a/psutil/arch/netbsd/specific.h b/psutil/arch/netbsd/specific.h deleted file mode 100644 index 391ed164..00000000 --- a/psutil/arch/netbsd/specific.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil. - * All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include - -typedef struct kinfo_proc2 kinfo_proc; - -int psutil_kinfo_proc(pid_t pid, kinfo_proc *proc); -struct kinfo_file * kinfo_getfile(pid_t pid, int* cnt); -int psutil_get_proc_list(kinfo_proc **procList, size_t *procCount); -char *psutil_get_cmd_args(pid_t pid, size_t *argsize); - -// -PyObject *psutil_get_cmdline(pid_t pid); -PyObject *psutil_proc_threads(PyObject *self, PyObject *args); -PyObject *psutil_virtual_mem(PyObject *self, PyObject *args); -PyObject *psutil_swap_mem(PyObject *self, PyObject *args); -PyObject *psutil_proc_num_fds(PyObject *self, PyObject *args); -PyObject *psutil_proc_connections(PyObject *self, PyObject *args); -PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args); -PyObject* psutil_disk_io_counters(PyObject* self, PyObject* args); -PyObject* psutil_proc_exe(PyObject* self, PyObject* args); -PyObject* psutil_proc_num_threads(PyObject* self, PyObject* args); -PyObject* psutil_cpu_stats(PyObject* self, PyObject* args); -PyObject *psutil_proc_cwd(PyObject *self, PyObject *args); diff --git a/setup.py b/setup.py index c69f6e3d..9fda1b30 100755 --- a/setup.py +++ b/setup.py @@ -287,7 +287,10 @@ elif NETBSD: 'psutil._psutil_bsd', sources=sources + [ 'psutil/_psutil_bsd.c', - 'psutil/arch/netbsd/specific.c', + 'psutil/arch/netbsd/cpu.c', + 'psutil/arch/netbsd/disk.c', + 'psutil/arch/netbsd/mem.c', + 'psutil/arch/netbsd/proc.c', 'psutil/arch/netbsd/socks.c', ], define_macros=macros, -- cgit v1.2.1