/* * 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(""); 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"); 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 = 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; 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 += swdev[i].se_nblks * DEV_BSIZE; swap_free += (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}; long pagesize = getpagesize(); 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 ); }