1 /*
2  * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  *
6  * Helper functions specific to FreeBSD.
7  * Used by _psutil_bsd module methods.
8  */
9 
10 #include <Python.h>
11 #include <assert.h>
12 #include <errno.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #include <sys/sysctl.h>
18 #include <sys/param.h>
19 #include <sys/user.h>
20 #include <sys/proc.h>
21 #include <signal.h>
22 #include <fcntl.h>
23 #include <sys/vmmeter.h>  // needed for vmtotal struct
24 #include <devstat.h>  // for swap mem
25 #include <libutil.h>  // process open files, shared libs (kinfo_getvmmap), cwd
26 #include <sys/cpuset.h>
27 
28 #include "../../_psutil_common.h"
29 #include "../../_psutil_posix.h"
30 
31 
32 #define PSUTIL_TV2DOUBLE(t)    ((t).tv_sec + (t).tv_usec / 1000000.0)
33 #define PSUTIL_BT2MSEC(bt) (bt.sec * 1000 + (((uint64_t) 1000000000 * (uint32_t) \
34         (bt.frac >> 32) ) >> 32 ) / 1000000)
35 #define DECIKELVIN_2_CELCIUS(t) (t - 2731) / 10
36 #ifndef _PATH_DEVNULL
37 #define _PATH_DEVNULL "/dev/null"
38 #endif
39 
40 
41 // ============================================================================
42 // Utility functions
43 // ============================================================================
44 
45 
46 int
psutil_kinfo_proc(pid_t pid,struct kinfo_proc * proc)47 psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) {
48     // Fills a kinfo_proc struct based on process pid.
49     int mib[4];
50     size_t size;
51     mib[0] = CTL_KERN;
52     mib[1] = KERN_PROC;
53     mib[2] = KERN_PROC_PID;
54     mib[3] = pid;
55 
56     size = sizeof(struct kinfo_proc);
57     if (sysctl((int *)mib, 4, proc, &size, NULL, 0) == -1) {
58         PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_PID)");
59         return -1;
60     }
61 
62     // sysctl stores 0 in the size if we can't find the process information.
63     if (size == 0) {
64         NoSuchProcess("sysctl (size = 0)");
65         return -1;
66     }
67     return 0;
68 }
69 
70 
71 // remove spaces from string
psutil_remove_spaces(char * str)72 static void psutil_remove_spaces(char *str) {
73     char *p1 = str;
74     char *p2 = str;
75     do
76         while (*p2 == ' ')
77             p2++;
78     while ((*p1++ = *p2++));
79 }
80 
81 
82 // ============================================================================
83 // APIS
84 // ============================================================================
85 
86 int
psutil_get_proc_list(struct kinfo_proc ** procList,size_t * procCount)87 psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) {
88     // Returns a list of all BSD processes on the system.  This routine
89     // allocates the list and puts it in *procList and a count of the
90     // number of entries in *procCount.  You are responsible for freeing
91     // this list. On success returns 0, else 1 with exception set.
92     int err;
93     struct kinfo_proc *buf = NULL;
94     int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 };
95     size_t length = 0;
96 
97     assert(procList != NULL);
98     assert(*procList == NULL);
99     assert(procCount != NULL);
100 
101     // Call sysctl with a NULL buffer in order to get buffer length.
102     err = sysctl(name, 3, NULL, &length, NULL, 0);
103     if (err == -1) {
104         PyErr_SetFromOSErrnoWithSyscall("sysctl (null buffer)");
105         return 1;
106     }
107 
108     // Allocate an appropriately sized buffer based on the results
109     // from the previous call.
110     buf = malloc(length);
111     if (buf == NULL) {
112         PyErr_NoMemory();
113         return 1;
114     }
115 
116     // Call sysctl again with the new buffer.
117     err = sysctl(name, 3, buf, &length, NULL, 0);
118     if (err == -1) {
119         PyErr_SetFromOSErrnoWithSyscall("sysctl");
120         free(buf);
121         return 1;
122     }
123 
124     *procList = buf;
125     *procCount = length / sizeof(struct kinfo_proc);
126     return 0;
127 }
128 
129 
130 /*
131  * XXX no longer used; it probably makese sense to remove it.
132  * Borrowed from psi Python System Information project
133  *
134  * Get command arguments and environment variables.
135  *
136  * Based on code from ps.
137  *
138  * Returns:
139  *      0 for success;
140  *      -1 for failure (Exception raised);
141  *      1 for insufficient privileges.
142  */
143 static char
psutil_get_cmd_args(pid_t pid,size_t * argsize)144 *psutil_get_cmd_args(pid_t pid, size_t *argsize) {
145     int mib[4];
146     int argmax;
147     size_t size = sizeof(argmax);
148     char *procargs = NULL;
149 
150     // Get the maximum process arguments size.
151     mib[0] = CTL_KERN;
152     mib[1] = KERN_ARGMAX;
153 
154     size = sizeof(argmax);
155     if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1)
156         return NULL;
157 
158     // Allocate space for the arguments.
159     procargs = (char *)malloc(argmax);
160     if (procargs == NULL) {
161         PyErr_NoMemory();
162         return NULL;
163     }
164 
165     // Make a sysctl() call to get the raw argument space of the process.
166     mib[0] = CTL_KERN;
167     mib[1] = KERN_PROC;
168     mib[2] = KERN_PROC_ARGS;
169     mib[3] = pid;
170 
171     size = argmax;
172     if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) {
173         free(procargs);
174         PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ARGS)");
175         return NULL;
176     }
177 
178     // return string and set the length of arguments
179     *argsize = size;
180     return procargs;
181 }
182 
183 
184 // returns the command line as a python list object
185 PyObject *
psutil_get_cmdline(pid_t pid)186 psutil_get_cmdline(pid_t pid) {
187     char *argstr = NULL;
188     size_t pos = 0;
189     size_t argsize = 0;
190     PyObject *py_retlist = Py_BuildValue("[]");
191     PyObject *py_arg = NULL;
192 
193     if (pid < 0)
194         return py_retlist;
195     argstr = psutil_get_cmd_args(pid, &argsize);
196     if (argstr == NULL)
197         goto error;
198 
199     // args are returned as a flattened string with \0 separators between
200     // arguments add each string to the list then step forward to the next
201     // separator
202     if (argsize > 0) {
203         while (pos < argsize) {
204             py_arg = PyUnicode_DecodeFSDefault(&argstr[pos]);
205             if (!py_arg)
206                 goto error;
207             if (PyList_Append(py_retlist, py_arg))
208                 goto error;
209             Py_DECREF(py_arg);
210             pos = pos + strlen(&argstr[pos]) + 1;
211         }
212     }
213 
214     free(argstr);
215     return py_retlist;
216 
217 error:
218     Py_XDECREF(py_arg);
219     Py_DECREF(py_retlist);
220     if (argstr != NULL)
221         free(argstr);
222     return NULL;
223 }
224 
225 
226 /*
227  * Return process pathname executable.
228  * Thanks to Robert N. M. Watson:
229  * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT
230  */
231 PyObject *
psutil_proc_exe(PyObject * self,PyObject * args)232 psutil_proc_exe(PyObject *self, PyObject *args) {
233     pid_t pid;
234     char pathname[PATH_MAX];
235     int error;
236     int mib[4];
237     int ret;
238     size_t size;
239 
240     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
241         return NULL;
242 
243     mib[0] = CTL_KERN;
244     mib[1] = KERN_PROC;
245     mib[2] = KERN_PROC_PATHNAME;
246     mib[3] = pid;
247 
248     size = sizeof(pathname);
249     error = sysctl(mib, 4, pathname, &size, NULL, 0);
250     if (error == -1) {
251         // see: https://github.com/giampaolo/psutil/issues/907
252         if (errno == ENOENT) {
253             return PyUnicode_DecodeFSDefault("");
254         }
255         else {
256             return \
257                 PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_PATHNAME)");
258         }
259     }
260     if (size == 0 || strlen(pathname) == 0) {
261         ret = psutil_pid_exists(pid);
262         if (ret == -1)
263             return NULL;
264         else if (ret == 0)
265             return NoSuchProcess("psutil_pid_exists");
266         else
267             strcpy(pathname, "");
268     }
269 
270     return PyUnicode_DecodeFSDefault(pathname);
271 }
272 
273 
274 PyObject *
psutil_proc_num_threads(PyObject * self,PyObject * args)275 psutil_proc_num_threads(PyObject *self, PyObject *args) {
276     // Return number of threads used by process as a Python integer.
277     pid_t pid;
278     struct kinfo_proc kp;
279     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
280         return NULL;
281     if (psutil_kinfo_proc(pid, &kp) == -1)
282         return NULL;
283     return Py_BuildValue("l", (long)kp.ki_numthreads);
284 }
285 
286 
287 PyObject *
psutil_proc_threads(PyObject * self,PyObject * args)288 psutil_proc_threads(PyObject *self, PyObject *args) {
289     // Retrieves all threads used by process returning a list of tuples
290     // including thread id, user time and system time.
291     // Thanks to Robert N. M. Watson:
292     // http://code.metager.de/source/xref/freebsd/usr.bin/procstat/
293     //     procstat_threads.c
294     pid_t pid;
295     int mib[4];
296     struct kinfo_proc *kip = NULL;
297     struct kinfo_proc *kipp = NULL;
298     int error;
299     unsigned int i;
300     size_t size;
301     PyObject *py_retlist = PyList_New(0);
302     PyObject *py_tuple = NULL;
303 
304     if (py_retlist == NULL)
305         return NULL;
306     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
307         goto error;
308 
309     // we need to re-query for thread information, so don't use *kipp
310     mib[0] = CTL_KERN;
311     mib[1] = KERN_PROC;
312     mib[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD;
313     mib[3] = pid;
314 
315     size = 0;
316     error = sysctl(mib, 4, NULL, &size, NULL, 0);
317     if (error == -1) {
318         PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_INC_THREAD)");
319         goto error;
320     }
321     if (size == 0) {
322         NoSuchProcess("sysctl (size = 0)");
323         goto error;
324     }
325 
326     kip = malloc(size);
327     if (kip == NULL) {
328         PyErr_NoMemory();
329         goto error;
330     }
331 
332     error = sysctl(mib, 4, kip, &size, NULL, 0);
333     if (error == -1) {
334         PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_INC_THREAD)");
335         goto error;
336     }
337     if (size == 0) {
338         NoSuchProcess("sysctl (size = 0)");
339         goto error;
340     }
341 
342     for (i = 0; i < size / sizeof(*kipp); i++) {
343         kipp = &kip[i];
344         py_tuple = Py_BuildValue("Idd",
345                                  kipp->ki_tid,
346                                  PSUTIL_TV2DOUBLE(kipp->ki_rusage.ru_utime),
347                                  PSUTIL_TV2DOUBLE(kipp->ki_rusage.ru_stime));
348         if (py_tuple == NULL)
349             goto error;
350         if (PyList_Append(py_retlist, py_tuple))
351             goto error;
352         Py_DECREF(py_tuple);
353     }
354     free(kip);
355     return py_retlist;
356 
357 error:
358     Py_XDECREF(py_tuple);
359     Py_DECREF(py_retlist);
360     if (kip != NULL)
361         free(kip);
362     return NULL;
363 }
364 
365 
366 PyObject *
psutil_cpu_count_phys(PyObject * self,PyObject * args)367 psutil_cpu_count_phys(PyObject *self, PyObject *args) {
368     // Return an XML string from which we'll determine the number of
369     // physical CPU cores in the system.
370     void *topology = NULL;
371     size_t size = 0;
372     PyObject *py_str;
373 
374     if (sysctlbyname("kern.sched.topology_spec", NULL, &size, NULL, 0))
375         goto error;
376 
377     topology = malloc(size);
378     if (!topology) {
379         PyErr_NoMemory();
380         return NULL;
381     }
382 
383     if (sysctlbyname("kern.sched.topology_spec", topology, &size, NULL, 0))
384         goto error;
385 
386     py_str = Py_BuildValue("s", topology);
387     free(topology);
388     return py_str;
389 
390 error:
391     if (topology != NULL)
392         free(topology);
393     Py_RETURN_NONE;
394 }
395 
396 
397 /*
398  * Return virtual memory usage statistics.
399  */
400 PyObject *
psutil_virtual_mem(PyObject * self,PyObject * args)401 psutil_virtual_mem(PyObject *self, PyObject *args) {
402     unsigned long  total;
403     unsigned int   active, inactive, wired, cached, free;
404     size_t         size = sizeof(total);
405     struct vmtotal vm;
406     int            mib[] = {CTL_VM, VM_METER};
407     long           pagesize = getpagesize();
408 #if __FreeBSD_version > 702101
409     long buffers;
410 #else
411     int buffers;
412 #endif
413     size_t buffers_size = sizeof(buffers);
414 
415     if (sysctlbyname("hw.physmem", &total, &size, NULL, 0)) {
416         return PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('hw.physmem')");
417     }
418     if (sysctlbyname("vm.stats.vm.v_active_count", &active, &size, NULL, 0)) {
419         return PyErr_SetFromOSErrnoWithSyscall(
420             "sysctlbyname('vm.stats.vm.v_active_count')");
421     }
422     if (sysctlbyname("vm.stats.vm.v_inactive_count", &inactive, &size, NULL, 0))
423     {
424         return PyErr_SetFromOSErrnoWithSyscall(
425             "sysctlbyname('vm.stats.vm.v_inactive_count')");
426     }
427     if (sysctlbyname("vm.stats.vm.v_wire_count", &wired, &size, NULL, 0)) {
428         return PyErr_SetFromOSErrnoWithSyscall(
429             "sysctlbyname('vm.stats.vm.v_wire_count')");
430     }
431     // https://github.com/giampaolo/psutil/issues/997
432     if (sysctlbyname("vm.stats.vm.v_cache_count", &cached, &size, NULL, 0)) {
433         cached = 0;
434     }
435     if (sysctlbyname("vm.stats.vm.v_free_count", &free, &size, NULL, 0)) {
436         return PyErr_SetFromOSErrnoWithSyscall(
437             "sysctlbyname('vm.stats.vm.v_free_count')");
438     }
439     if (sysctlbyname("vfs.bufspace", &buffers, &buffers_size, NULL, 0)) {
440         return PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('vfs.bufspace')");
441     }
442 
443     size = sizeof(vm);
444     if (sysctl(mib, 2, &vm, &size, NULL, 0) != 0) {
445         return PyErr_SetFromOSErrnoWithSyscall("sysctl(CTL_VM | VM_METER)");
446     }
447 
448     return Py_BuildValue("KKKKKKKK",
449         (unsigned long long) total,
450         (unsigned long long) free     * pagesize,
451         (unsigned long long) active   * pagesize,
452         (unsigned long long) inactive * pagesize,
453         (unsigned long long) wired    * pagesize,
454         (unsigned long long) cached   * pagesize,
455         (unsigned long long) buffers,
456         (unsigned long long) (vm.t_vmshr + vm.t_rmshr) * pagesize  // shared
457     );
458 }
459 
460 
461 PyObject *
psutil_swap_mem(PyObject * self,PyObject * args)462 psutil_swap_mem(PyObject *self, PyObject *args) {
463     // Return swap memory stats (see 'swapinfo' cmdline tool)
464     kvm_t *kd;
465     struct kvm_swap kvmsw[1];
466     unsigned int swapin, swapout, nodein, nodeout;
467     size_t size = sizeof(unsigned int);
468     int pagesize;
469 
470     kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open failed");
471     if (kd == NULL) {
472         PyErr_SetString(PyExc_RuntimeError, "kvm_open() syscall failed");
473         return NULL;
474     }
475 
476     if (kvm_getswapinfo(kd, kvmsw, 1, 0) < 0) {
477         kvm_close(kd);
478         PyErr_SetString(PyExc_RuntimeError,
479                         "kvm_getswapinfo() syscall failed");
480         return NULL;
481     }
482 
483     kvm_close(kd);
484 
485     if (sysctlbyname("vm.stats.vm.v_swapin", &swapin, &size, NULL, 0) == -1) {
486         return PyErr_SetFromOSErrnoWithSyscall(
487             "sysctlbyname('vm.stats.vm.v_swapin)'");
488     }
489     if (sysctlbyname("vm.stats.vm.v_swapout", &swapout, &size, NULL, 0) == -1){
490         return PyErr_SetFromOSErrnoWithSyscall(
491             "sysctlbyname('vm.stats.vm.v_swapout)'");
492     }
493     if (sysctlbyname("vm.stats.vm.v_vnodein", &nodein, &size, NULL, 0) == -1) {
494         return PyErr_SetFromOSErrnoWithSyscall(
495             "sysctlbyname('vm.stats.vm.v_vnodein)'");
496     }
497     if (sysctlbyname("vm.stats.vm.v_vnodeout", &nodeout, &size, NULL, 0) == -1) {
498         return PyErr_SetFromOSErrnoWithSyscall(
499             "sysctlbyname('vm.stats.vm.v_vnodeout)'");
500     }
501 
502     pagesize = getpagesize();
503     if (pagesize <= 0) {
504         PyErr_SetString(PyExc_ValueError, "invalid getpagesize()");
505         return NULL;
506     }
507 
508     return Py_BuildValue(
509         "(KKKII)",
510         (unsigned long long)kvmsw[0].ksw_total * pagesize,  // total
511         (unsigned long long)kvmsw[0].ksw_used * pagesize,  // used
512         (unsigned long long)kvmsw[0].ksw_total * pagesize - // free
513                                 kvmsw[0].ksw_used * pagesize,
514         swapin + swapout,  // swap in
515         nodein + nodeout  // swap out
516     );
517 }
518 
519 
520 #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
521 PyObject *
psutil_proc_cwd(PyObject * self,PyObject * args)522 psutil_proc_cwd(PyObject *self, PyObject *args) {
523     pid_t pid;
524     struct kinfo_file *freep = NULL;
525     struct kinfo_file *kif;
526     struct kinfo_proc kipp;
527     PyObject *py_path = NULL;
528 
529     int i, cnt;
530 
531     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
532         goto error;
533     if (psutil_kinfo_proc(pid, &kipp) == -1)
534         goto error;
535 
536     errno = 0;
537     freep = kinfo_getfile(pid, &cnt);
538     if (freep == NULL) {
539         psutil_raise_for_pid(pid, "kinfo_getfile()");
540         goto error;
541     }
542 
543     for (i = 0; i < cnt; i++) {
544         kif = &freep[i];
545         if (kif->kf_fd == KF_FD_TYPE_CWD) {
546             py_path = PyUnicode_DecodeFSDefault(kif->kf_path);
547             if (!py_path)
548                 goto error;
549             break;
550         }
551     }
552     /*
553      * For lower pids it seems we can't retrieve any information
554      * (lsof can't do that it either).  Since this happens even
555      * as root we return an empty string instead of AccessDenied.
556      */
557     if (py_path == NULL)
558         py_path = PyUnicode_DecodeFSDefault("");
559     free(freep);
560     return py_path;
561 
562 error:
563     Py_XDECREF(py_path);
564     if (freep != NULL)
565         free(freep);
566     return NULL;
567 }
568 #endif
569 
570 
571 #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
572 PyObject *
psutil_proc_num_fds(PyObject * self,PyObject * args)573 psutil_proc_num_fds(PyObject *self, PyObject *args) {
574     pid_t pid;
575     int cnt;
576 
577     struct kinfo_file *freep;
578     struct kinfo_proc kipp;
579 
580     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
581         return NULL;
582     if (psutil_kinfo_proc(pid, &kipp) == -1)
583         return NULL;
584 
585     errno = 0;
586     freep = kinfo_getfile(pid, &cnt);
587     if (freep == NULL) {
588         psutil_raise_for_pid(pid, "kinfo_getfile()");
589         return NULL;
590     }
591     free(freep);
592 
593     return Py_BuildValue("i", cnt);
594 }
595 #endif
596 
597 
598 PyObject *
psutil_per_cpu_times(PyObject * self,PyObject * args)599 psutil_per_cpu_times(PyObject *self, PyObject *args) {
600     static int maxcpus;
601     int mib[2];
602     int ncpu;
603     size_t len;
604     size_t size;
605     int i;
606     PyObject *py_retlist = PyList_New(0);
607     PyObject *py_cputime = NULL;
608 
609     if (py_retlist == NULL)
610         return NULL;
611 
612     // retrieve maxcpus value
613     size = sizeof(maxcpus);
614     if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) {
615         Py_DECREF(py_retlist);
616         return PyErr_SetFromOSErrnoWithSyscall(
617             "sysctlbyname('kern.smp.maxcpus')");
618     }
619     long cpu_time[maxcpus][CPUSTATES];
620 
621     // retrieve the number of cpus
622     mib[0] = CTL_HW;
623     mib[1] = HW_NCPU;
624     len = sizeof(ncpu);
625     if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
626         PyErr_SetFromOSErrnoWithSyscall("sysctl(HW_NCPU)");
627         goto error;
628     }
629 
630     // per-cpu info
631     size = sizeof(cpu_time);
632     if (sysctlbyname("kern.cp_times", &cpu_time, &size, NULL, 0) == -1) {
633         PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('kern.smp.maxcpus')");
634         goto error;
635     }
636 
637     for (i = 0; i < ncpu; i++) {
638         py_cputime = Py_BuildValue(
639             "(ddddd)",
640             (double)cpu_time[i][CP_USER] / CLOCKS_PER_SEC,
641             (double)cpu_time[i][CP_NICE] / CLOCKS_PER_SEC,
642             (double)cpu_time[i][CP_SYS] / CLOCKS_PER_SEC,
643             (double)cpu_time[i][CP_IDLE] / CLOCKS_PER_SEC,
644             (double)cpu_time[i][CP_INTR] / CLOCKS_PER_SEC);
645         if (!py_cputime)
646             goto error;
647         if (PyList_Append(py_retlist, py_cputime))
648             goto error;
649         Py_DECREF(py_cputime);
650     }
651 
652     return py_retlist;
653 
654 error:
655     Py_XDECREF(py_cputime);
656     Py_DECREF(py_retlist);
657     return NULL;
658 }
659 
660 
661 PyObject *
psutil_disk_io_counters(PyObject * self,PyObject * args)662 psutil_disk_io_counters(PyObject *self, PyObject *args) {
663     int i;
664     struct statinfo stats;
665 
666     PyObject *py_retdict = PyDict_New();
667     PyObject *py_disk_info = NULL;
668 
669     if (py_retdict == NULL)
670         return NULL;
671     if (devstat_checkversion(NULL) < 0) {
672         PyErr_Format(PyExc_RuntimeError,
673                      "devstat_checkversion() syscall failed");
674         goto error;
675     }
676 
677     stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
678     if (stats.dinfo == NULL) {
679         PyErr_NoMemory();
680         goto error;
681     }
682     bzero(stats.dinfo, sizeof(struct devinfo));
683 
684     if (devstat_getdevs(NULL, &stats) == -1) {
685         PyErr_Format(PyExc_RuntimeError, "devstat_getdevs() syscall failed");
686         goto error;
687     }
688 
689     for (i = 0; i < stats.dinfo->numdevs; i++) {
690         py_disk_info = NULL;
691         struct devstat current;
692         char disk_name[128];
693         current = stats.dinfo->devices[i];
694         snprintf(disk_name, sizeof(disk_name), "%s%d",
695                  current.device_name,
696                  current.unit_number);
697 
698         py_disk_info = Py_BuildValue(
699             "(KKKKLLL)",
700             current.operations[DEVSTAT_READ],   // no reads
701             current.operations[DEVSTAT_WRITE],  // no writes
702             current.bytes[DEVSTAT_READ],        // bytes read
703             current.bytes[DEVSTAT_WRITE],       // bytes written
704             (long long) PSUTIL_BT2MSEC(current.duration[DEVSTAT_READ]),  // r time
705             (long long) PSUTIL_BT2MSEC(current.duration[DEVSTAT_WRITE]),  // w time
706             (long long) PSUTIL_BT2MSEC(current.busy_time)  // busy time
707         );      // finished transactions
708         if (!py_disk_info)
709             goto error;
710         if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
711             goto error;
712         Py_DECREF(py_disk_info);
713     }
714 
715     if (stats.dinfo->mem_ptr)
716         free(stats.dinfo->mem_ptr);
717     free(stats.dinfo);
718     return py_retdict;
719 
720 error:
721     Py_XDECREF(py_disk_info);
722     Py_DECREF(py_retdict);
723     if (stats.dinfo != NULL)
724         free(stats.dinfo);
725     return NULL;
726 }
727 
728 
729 PyObject *
psutil_proc_memory_maps(PyObject * self,PyObject * args)730 psutil_proc_memory_maps(PyObject *self, PyObject *args) {
731     // Return a list of tuples for every process memory maps.
732     // 'procstat' cmdline utility has been used as an example.
733     pid_t pid;
734     int ptrwidth;
735     int i, cnt;
736     char addr[1000];
737     char perms[4];
738     char *path;
739     struct kinfo_proc kp;
740     struct kinfo_vmentry *freep = NULL;
741     struct kinfo_vmentry *kve;
742     ptrwidth = 2 * sizeof(void *);
743     PyObject *py_tuple = NULL;
744     PyObject *py_path = NULL;
745     PyObject *py_retlist = PyList_New(0);
746 
747     if (py_retlist == NULL)
748         return NULL;
749     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
750         goto error;
751     if (psutil_kinfo_proc(pid, &kp) == -1)
752         goto error;
753 
754     errno = 0;
755     freep = kinfo_getvmmap(pid, &cnt);
756     if (freep == NULL) {
757         psutil_raise_for_pid(pid, "kinfo_getvmmap()");
758         goto error;
759     }
760     for (i = 0; i < cnt; i++) {
761         py_tuple = NULL;
762         kve = &freep[i];
763         addr[0] = '\0';
764         perms[0] = '\0';
765         sprintf(addr, "%#*jx-%#*jx", ptrwidth, (uintmax_t)kve->kve_start,
766                 ptrwidth, (uintmax_t)kve->kve_end);
767         psutil_remove_spaces(addr);
768         strlcat(perms, kve->kve_protection & KVME_PROT_READ ? "r" : "-",
769                 sizeof(perms));
770         strlcat(perms, kve->kve_protection & KVME_PROT_WRITE ? "w" : "-",
771                 sizeof(perms));
772         strlcat(perms, kve->kve_protection & KVME_PROT_EXEC ? "x" : "-",
773                 sizeof(perms));
774 
775         if (strlen(kve->kve_path) == 0) {
776             switch (kve->kve_type) {
777                 case KVME_TYPE_NONE:
778                     path = "[none]";
779                     break;
780                 case KVME_TYPE_DEFAULT:
781                     path = "[default]";
782                     break;
783                 case KVME_TYPE_VNODE:
784                     path = "[vnode]";
785                     break;
786                 case KVME_TYPE_SWAP:
787                     path = "[swap]";
788                     break;
789                 case KVME_TYPE_DEVICE:
790                     path = "[device]";
791                     break;
792                 case KVME_TYPE_PHYS:
793                     path = "[phys]";
794                     break;
795                 case KVME_TYPE_DEAD:
796                     path = "[dead]";
797                     break;
798                 case KVME_TYPE_SG:
799                     path = "[sg]";
800                     break;
801                 case KVME_TYPE_UNKNOWN:
802                     path = "[unknown]";
803                     break;
804                 default:
805                     path = "[?]";
806                     break;
807             }
808         }
809         else {
810             path = kve->kve_path;
811         }
812 
813         py_path = PyUnicode_DecodeFSDefault(path);
814         if (! py_path)
815             goto error;
816         py_tuple = Py_BuildValue("ssOiiii",
817             addr,                       // "start-end" address
818             perms,                      // "rwx" permissions
819             py_path,                    // path
820             kve->kve_resident,          // rss
821             kve->kve_private_resident,  // private
822             kve->kve_ref_count,         // ref count
823             kve->kve_shadow_count);     // shadow count
824         if (!py_tuple)
825             goto error;
826         if (PyList_Append(py_retlist, py_tuple))
827             goto error;
828         Py_DECREF(py_path);
829         Py_DECREF(py_tuple);
830     }
831     free(freep);
832     return py_retlist;
833 
834 error:
835     Py_XDECREF(py_tuple);
836     Py_XDECREF(py_path);
837     Py_DECREF(py_retlist);
838     if (freep != NULL)
839         free(freep);
840     return NULL;
841 }
842 
843 
844 PyObject*
psutil_proc_cpu_affinity_get(PyObject * self,PyObject * args)845 psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args) {
846     // Get process CPU affinity.
847     // Reference:
848     // http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c
849     pid_t pid;
850     int ret;
851     int i;
852     cpuset_t mask;
853     PyObject* py_retlist;
854     PyObject* py_cpu_num;
855 
856     if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
857         return NULL;
858     ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
859                              sizeof(mask), &mask);
860     if (ret != 0)
861         return PyErr_SetFromErrno(PyExc_OSError);
862 
863     py_retlist = PyList_New(0);
864     if (py_retlist == NULL)
865         return NULL;
866 
867     for (i = 0; i < CPU_SETSIZE; i++) {
868         if (CPU_ISSET(i, &mask)) {
869             py_cpu_num = Py_BuildValue("i", i);
870             if (py_cpu_num == NULL)
871                 goto error;
872             if (PyList_Append(py_retlist, py_cpu_num))
873                 goto error;
874         }
875     }
876 
877     return py_retlist;
878 
879 error:
880     Py_XDECREF(py_cpu_num);
881     Py_DECREF(py_retlist);
882     return NULL;
883 }
884 
885 
886 PyObject *
psutil_proc_cpu_affinity_set(PyObject * self,PyObject * args)887 psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
888     // Set process CPU affinity.
889     // Reference:
890     // http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c
891     pid_t pid;
892     int i;
893     int seq_len;
894     int ret;
895     cpuset_t cpu_set;
896     PyObject *py_cpu_set;
897     PyObject *py_cpu_seq = NULL;
898 
899     if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &py_cpu_set))
900         return NULL;
901 
902     py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer");
903     if (!py_cpu_seq)
904         return NULL;
905     seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);
906 
907     // calculate the mask
908     CPU_ZERO(&cpu_set);
909     for (i = 0; i < seq_len; i++) {
910         PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);
911 #if PY_MAJOR_VERSION >= 3
912         long value = PyLong_AsLong(item);
913 #else
914         long value = PyInt_AsLong(item);
915 #endif
916         if (value == -1 || PyErr_Occurred())
917             goto error;
918         CPU_SET(value, &cpu_set);
919     }
920 
921     // set affinity
922     ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
923                              sizeof(cpu_set), &cpu_set);
924     if (ret != 0) {
925         PyErr_SetFromErrno(PyExc_OSError);
926         goto error;
927     }
928 
929     Py_DECREF(py_cpu_seq);
930     Py_RETURN_NONE;
931 
932 error:
933     if (py_cpu_seq != NULL)
934         Py_DECREF(py_cpu_seq);
935     return NULL;
936 }
937 
938 
939 PyObject *
psutil_cpu_stats(PyObject * self,PyObject * args)940 psutil_cpu_stats(PyObject *self, PyObject *args) {
941     unsigned int v_soft;
942     unsigned int v_intr;
943     unsigned int v_syscall;
944     unsigned int v_trap;
945     unsigned int v_swtch;
946     size_t size = sizeof(v_soft);
947 
948     if (sysctlbyname("vm.stats.sys.v_soft", &v_soft, &size, NULL, 0)) {
949         return PyErr_SetFromOSErrnoWithSyscall(
950             "sysctlbyname('vm.stats.sys.v_soft')");
951     }
952     if (sysctlbyname("vm.stats.sys.v_intr", &v_intr, &size, NULL, 0)) {
953         return PyErr_SetFromOSErrnoWithSyscall(
954             "sysctlbyname('vm.stats.sys.v_intr')");
955     }
956     if (sysctlbyname("vm.stats.sys.v_syscall", &v_syscall, &size, NULL, 0)) {
957         return PyErr_SetFromOSErrnoWithSyscall(
958             "sysctlbyname('vm.stats.sys.v_syscall')");
959     }
960     if (sysctlbyname("vm.stats.sys.v_trap", &v_trap, &size, NULL, 0)) {
961         return PyErr_SetFromOSErrnoWithSyscall(
962             "sysctlbyname('vm.stats.sys.v_trap')");
963     }
964     if (sysctlbyname("vm.stats.sys.v_swtch", &v_swtch, &size, NULL, 0)) {
965         return PyErr_SetFromOSErrnoWithSyscall(
966             "sysctlbyname('vm.stats.sys.v_swtch')");
967     }
968 
969     return Py_BuildValue(
970         "IIIII",
971         v_swtch,  // ctx switches
972         v_intr,  // interrupts
973         v_soft,  // software interrupts
974         v_syscall,  // syscalls
975         v_trap  // traps
976     );
977 }
978 
979 
980 /*
981  * Return battery information.
982  */
983 PyObject *
psutil_sensors_battery(PyObject * self,PyObject * args)984 psutil_sensors_battery(PyObject *self, PyObject *args) {
985     int percent;
986     int minsleft;
987     int power_plugged;
988     size_t size = sizeof(percent);
989 
990     if (sysctlbyname("hw.acpi.battery.life", &percent, &size, NULL, 0))
991         goto error;
992     if (sysctlbyname("hw.acpi.battery.time", &minsleft, &size, NULL, 0))
993         goto error;
994     if (sysctlbyname("hw.acpi.acline", &power_plugged, &size, NULL, 0))
995         goto error;
996     return Py_BuildValue("iii", percent, minsleft, power_plugged);
997 
998 error:
999     // see: https://github.com/giampaolo/psutil/issues/1074
1000     if (errno == ENOENT)
1001         PyErr_SetString(PyExc_NotImplementedError, "no battery");
1002     else
1003         PyErr_SetFromErrno(PyExc_OSError);
1004     return NULL;
1005 }
1006 
1007 
1008 /*
1009  * Return temperature information for a given CPU core number.
1010  */
1011 PyObject *
psutil_sensors_cpu_temperature(PyObject * self,PyObject * args)1012 psutil_sensors_cpu_temperature(PyObject *self, PyObject *args) {
1013     int current;
1014     int tjmax;
1015     int core;
1016     char sensor[26];
1017     size_t size = sizeof(current);
1018 
1019     if (! PyArg_ParseTuple(args, "i", &core))
1020         return NULL;
1021     sprintf(sensor, "dev.cpu.%d.temperature", core);
1022     if (sysctlbyname(sensor, &current, &size, NULL, 0))
1023         goto error;
1024     current = DECIKELVIN_2_CELCIUS(current);
1025 
1026     // Return -273 in case of faliure.
1027     sprintf(sensor, "dev.cpu.%d.coretemp.tjmax", core);
1028     if (sysctlbyname(sensor, &tjmax, &size, NULL, 0))
1029         tjmax = 0;
1030     tjmax = DECIKELVIN_2_CELCIUS(tjmax);
1031 
1032     return Py_BuildValue("ii", current, tjmax);
1033 
1034 error:
1035     if (errno == ENOENT)
1036         PyErr_SetString(PyExc_NotImplementedError, "no temperature sensors");
1037     else
1038         PyErr_SetFromErrno(PyExc_OSError);
1039     return NULL;
1040 }
1041 
1042 
1043 /*
1044  * Return frequency information of a given CPU.
1045  * As of Dec 2018 only CPU 0 appears to be supported and all other
1046  * cores match the frequency of CPU 0.
1047  */
1048 PyObject *
psutil_cpu_freq(PyObject * self,PyObject * args)1049 psutil_cpu_freq(PyObject *self, PyObject *args) {
1050     int current;
1051     int core;
1052     char sensor[26];
1053     char available_freq_levels[1000];
1054     size_t size = sizeof(current);
1055 
1056     if (! PyArg_ParseTuple(args, "i", &core))
1057         return NULL;
1058     // https://www.unix.com/man-page/FreeBSD/4/cpufreq/
1059     sprintf(sensor, "dev.cpu.%d.freq", core);
1060     if (sysctlbyname(sensor, &current, &size, NULL, 0))
1061         goto error;
1062 
1063     size = sizeof(available_freq_levels);
1064     // https://www.unix.com/man-page/FreeBSD/4/cpufreq/
1065     // In case of failure, an empty string is returned.
1066     sprintf(sensor, "dev.cpu.%d.freq_levels", core);
1067     sysctlbyname(sensor, &available_freq_levels, &size, NULL, 0);
1068 
1069     return Py_BuildValue("is", current, available_freq_levels);
1070 
1071 error:
1072     if (errno == ENOENT)
1073         PyErr_SetString(PyExc_NotImplementedError, "unable to read frequency");
1074     else
1075         PyErr_SetFromErrno(PyExc_OSError);
1076     return NULL;
1077 }
1078