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 -> 0");
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 = psutil_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     long pagesize = psutil_getpagesize();
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     return Py_BuildValue(
503         "(KKKII)",
504         (unsigned long long)kvmsw[0].ksw_total * pagesize,  // total
505         (unsigned long long)kvmsw[0].ksw_used * pagesize,  // used
506         (unsigned long long)kvmsw[0].ksw_total * pagesize - // free
507                                 kvmsw[0].ksw_used * pagesize,
508         swapin + swapout,  // swap in
509         nodein + nodeout  // swap out
510     );
511 }
512 
513 
514 #if defined(__FreeBSD_version) && __FreeBSD_version >= 701000
515 PyObject *
psutil_proc_cwd(PyObject * self,PyObject * args)516 psutil_proc_cwd(PyObject *self, PyObject *args) {
517     pid_t pid;
518     struct kinfo_file *freep = NULL;
519     struct kinfo_file *kif;
520     struct kinfo_proc kipp;
521     PyObject *py_path = NULL;
522 
523     int i, cnt;
524 
525     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
526         goto error;
527     if (psutil_kinfo_proc(pid, &kipp) == -1)
528         goto error;
529 
530     errno = 0;
531     freep = kinfo_getfile(pid, &cnt);
532     if (freep == NULL) {
533         psutil_raise_for_pid(pid, "kinfo_getfile()");
534         goto error;
535     }
536 
537     for (i = 0; i < cnt; i++) {
538         kif = &freep[i];
539         if (kif->kf_fd == KF_FD_TYPE_CWD) {
540             py_path = PyUnicode_DecodeFSDefault(kif->kf_path);
541             if (!py_path)
542                 goto error;
543             break;
544         }
545     }
546     /*
547      * For lower pids it seems we can't retrieve any information
548      * (lsof can't do that it either).  Since this happens even
549      * as root we return an empty string instead of AccessDenied.
550      */
551     if (py_path == NULL)
552         py_path = PyUnicode_DecodeFSDefault("");
553     free(freep);
554     return py_path;
555 
556 error:
557     Py_XDECREF(py_path);
558     if (freep != NULL)
559         free(freep);
560     return NULL;
561 }
562 #endif
563 
564 
565 #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
566 PyObject *
psutil_proc_num_fds(PyObject * self,PyObject * args)567 psutil_proc_num_fds(PyObject *self, PyObject *args) {
568     pid_t pid;
569     int cnt;
570 
571     struct kinfo_file *freep;
572     struct kinfo_proc kipp;
573 
574     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
575         return NULL;
576     if (psutil_kinfo_proc(pid, &kipp) == -1)
577         return NULL;
578 
579     errno = 0;
580     freep = kinfo_getfile(pid, &cnt);
581     if (freep == NULL) {
582         psutil_raise_for_pid(pid, "kinfo_getfile()");
583         return NULL;
584     }
585     free(freep);
586 
587     return Py_BuildValue("i", cnt);
588 }
589 #endif
590 
591 
592 PyObject *
psutil_per_cpu_times(PyObject * self,PyObject * args)593 psutil_per_cpu_times(PyObject *self, PyObject *args) {
594     static int maxcpus;
595     int mib[2];
596     int ncpu;
597     size_t len;
598     size_t size;
599     int i;
600     PyObject *py_retlist = PyList_New(0);
601     PyObject *py_cputime = NULL;
602 
603     if (py_retlist == NULL)
604         return NULL;
605 
606     // retrieve maxcpus value
607     size = sizeof(maxcpus);
608     if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) {
609         Py_DECREF(py_retlist);
610         return PyErr_SetFromOSErrnoWithSyscall(
611             "sysctlbyname('kern.smp.maxcpus')");
612     }
613     long cpu_time[maxcpus][CPUSTATES];
614 
615     // retrieve the number of cpus
616     mib[0] = CTL_HW;
617     mib[1] = HW_NCPU;
618     len = sizeof(ncpu);
619     if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
620         PyErr_SetFromOSErrnoWithSyscall("sysctl(HW_NCPU)");
621         goto error;
622     }
623 
624     // per-cpu info
625     size = sizeof(cpu_time);
626     if (sysctlbyname("kern.cp_times", &cpu_time, &size, NULL, 0) == -1) {
627         PyErr_SetFromOSErrnoWithSyscall("sysctlbyname('kern.smp.maxcpus')");
628         goto error;
629     }
630 
631     for (i = 0; i < ncpu; i++) {
632         py_cputime = Py_BuildValue(
633             "(ddddd)",
634             (double)cpu_time[i][CP_USER] / CLOCKS_PER_SEC,
635             (double)cpu_time[i][CP_NICE] / CLOCKS_PER_SEC,
636             (double)cpu_time[i][CP_SYS] / CLOCKS_PER_SEC,
637             (double)cpu_time[i][CP_IDLE] / CLOCKS_PER_SEC,
638             (double)cpu_time[i][CP_INTR] / CLOCKS_PER_SEC);
639         if (!py_cputime)
640             goto error;
641         if (PyList_Append(py_retlist, py_cputime))
642             goto error;
643         Py_DECREF(py_cputime);
644     }
645 
646     return py_retlist;
647 
648 error:
649     Py_XDECREF(py_cputime);
650     Py_DECREF(py_retlist);
651     return NULL;
652 }
653 
654 
655 PyObject *
psutil_disk_io_counters(PyObject * self,PyObject * args)656 psutil_disk_io_counters(PyObject *self, PyObject *args) {
657     int i;
658     struct statinfo stats;
659 
660     PyObject *py_retdict = PyDict_New();
661     PyObject *py_disk_info = NULL;
662 
663     if (py_retdict == NULL)
664         return NULL;
665     if (devstat_checkversion(NULL) < 0) {
666         PyErr_Format(PyExc_RuntimeError,
667                      "devstat_checkversion() syscall failed");
668         goto error;
669     }
670 
671     stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
672     if (stats.dinfo == NULL) {
673         PyErr_NoMemory();
674         goto error;
675     }
676     bzero(stats.dinfo, sizeof(struct devinfo));
677 
678     if (devstat_getdevs(NULL, &stats) == -1) {
679         PyErr_Format(PyExc_RuntimeError, "devstat_getdevs() syscall failed");
680         goto error;
681     }
682 
683     for (i = 0; i < stats.dinfo->numdevs; i++) {
684         py_disk_info = NULL;
685         struct devstat current;
686         char disk_name[128];
687         current = stats.dinfo->devices[i];
688         snprintf(disk_name, sizeof(disk_name), "%s%d",
689                  current.device_name,
690                  current.unit_number);
691 
692         py_disk_info = Py_BuildValue(
693             "(KKKKLLL)",
694             current.operations[DEVSTAT_READ],   // no reads
695             current.operations[DEVSTAT_WRITE],  // no writes
696             current.bytes[DEVSTAT_READ],        // bytes read
697             current.bytes[DEVSTAT_WRITE],       // bytes written
698             (long long) PSUTIL_BT2MSEC(current.duration[DEVSTAT_READ]),  // r time
699             (long long) PSUTIL_BT2MSEC(current.duration[DEVSTAT_WRITE]),  // w time
700             (long long) PSUTIL_BT2MSEC(current.busy_time)  // busy time
701         );      // finished transactions
702         if (!py_disk_info)
703             goto error;
704         if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
705             goto error;
706         Py_DECREF(py_disk_info);
707     }
708 
709     if (stats.dinfo->mem_ptr)
710         free(stats.dinfo->mem_ptr);
711     free(stats.dinfo);
712     return py_retdict;
713 
714 error:
715     Py_XDECREF(py_disk_info);
716     Py_DECREF(py_retdict);
717     if (stats.dinfo != NULL)
718         free(stats.dinfo);
719     return NULL;
720 }
721 
722 
723 PyObject *
psutil_proc_memory_maps(PyObject * self,PyObject * args)724 psutil_proc_memory_maps(PyObject *self, PyObject *args) {
725     // Return a list of tuples for every process memory maps.
726     // 'procstat' cmdline utility has been used as an example.
727     pid_t pid;
728     int ptrwidth;
729     int i, cnt;
730     char addr[1000];
731     char perms[4];
732     char *path;
733     struct kinfo_proc kp;
734     struct kinfo_vmentry *freep = NULL;
735     struct kinfo_vmentry *kve;
736     ptrwidth = 2 * sizeof(void *);
737     PyObject *py_tuple = NULL;
738     PyObject *py_path = NULL;
739     PyObject *py_retlist = PyList_New(0);
740 
741     if (py_retlist == NULL)
742         return NULL;
743     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
744         goto error;
745     if (psutil_kinfo_proc(pid, &kp) == -1)
746         goto error;
747 
748     errno = 0;
749     freep = kinfo_getvmmap(pid, &cnt);
750     if (freep == NULL) {
751         psutil_raise_for_pid(pid, "kinfo_getvmmap()");
752         goto error;
753     }
754     for (i = 0; i < cnt; i++) {
755         py_tuple = NULL;
756         kve = &freep[i];
757         addr[0] = '\0';
758         perms[0] = '\0';
759         sprintf(addr, "%#*jx-%#*jx", ptrwidth, (uintmax_t)kve->kve_start,
760                 ptrwidth, (uintmax_t)kve->kve_end);
761         psutil_remove_spaces(addr);
762         strlcat(perms, kve->kve_protection & KVME_PROT_READ ? "r" : "-",
763                 sizeof(perms));
764         strlcat(perms, kve->kve_protection & KVME_PROT_WRITE ? "w" : "-",
765                 sizeof(perms));
766         strlcat(perms, kve->kve_protection & KVME_PROT_EXEC ? "x" : "-",
767                 sizeof(perms));
768 
769         if (strlen(kve->kve_path) == 0) {
770             switch (kve->kve_type) {
771                 case KVME_TYPE_NONE:
772                     path = "[none]";
773                     break;
774                 case KVME_TYPE_DEFAULT:
775                     path = "[default]";
776                     break;
777                 case KVME_TYPE_VNODE:
778                     path = "[vnode]";
779                     break;
780                 case KVME_TYPE_SWAP:
781                     path = "[swap]";
782                     break;
783                 case KVME_TYPE_DEVICE:
784                     path = "[device]";
785                     break;
786                 case KVME_TYPE_PHYS:
787                     path = "[phys]";
788                     break;
789                 case KVME_TYPE_DEAD:
790                     path = "[dead]";
791                     break;
792 #ifdef KVME_TYPE_SG
793                 case KVME_TYPE_SG:
794                     path = "[sg]";
795                     break;
796 #endif
797                 case KVME_TYPE_UNKNOWN:
798                     path = "[unknown]";
799                     break;
800                 default:
801                     path = "[?]";
802                     break;
803             }
804         }
805         else {
806             path = kve->kve_path;
807         }
808 
809         py_path = PyUnicode_DecodeFSDefault(path);
810         if (! py_path)
811             goto error;
812         py_tuple = Py_BuildValue("ssOiiii",
813             addr,                       // "start-end" address
814             perms,                      // "rwx" permissions
815             py_path,                    // path
816             kve->kve_resident,          // rss
817             kve->kve_private_resident,  // private
818             kve->kve_ref_count,         // ref count
819             kve->kve_shadow_count);     // shadow count
820         if (!py_tuple)
821             goto error;
822         if (PyList_Append(py_retlist, py_tuple))
823             goto error;
824         Py_DECREF(py_path);
825         Py_DECREF(py_tuple);
826     }
827     free(freep);
828     return py_retlist;
829 
830 error:
831     Py_XDECREF(py_tuple);
832     Py_XDECREF(py_path);
833     Py_DECREF(py_retlist);
834     if (freep != NULL)
835         free(freep);
836     return NULL;
837 }
838 
839 
840 PyObject*
psutil_proc_cpu_affinity_get(PyObject * self,PyObject * args)841 psutil_proc_cpu_affinity_get(PyObject* self, PyObject* args) {
842     // Get process CPU affinity.
843     // Reference:
844     // http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c
845     pid_t pid;
846     int ret;
847     int i;
848     cpuset_t mask;
849     PyObject* py_retlist;
850     PyObject* py_cpu_num;
851 
852     if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
853         return NULL;
854     ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
855                              sizeof(mask), &mask);
856     if (ret != 0)
857         return PyErr_SetFromErrno(PyExc_OSError);
858 
859     py_retlist = PyList_New(0);
860     if (py_retlist == NULL)
861         return NULL;
862 
863     for (i = 0; i < CPU_SETSIZE; i++) {
864         if (CPU_ISSET(i, &mask)) {
865             py_cpu_num = Py_BuildValue("i", i);
866             if (py_cpu_num == NULL)
867                 goto error;
868             if (PyList_Append(py_retlist, py_cpu_num))
869                 goto error;
870         }
871     }
872 
873     return py_retlist;
874 
875 error:
876     Py_XDECREF(py_cpu_num);
877     Py_DECREF(py_retlist);
878     return NULL;
879 }
880 
881 
882 PyObject *
psutil_proc_cpu_affinity_set(PyObject * self,PyObject * args)883 psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
884     // Set process CPU affinity.
885     // Reference:
886     // http://sources.freebsd.org/RELENG_9/src/usr.bin/cpuset/cpuset.c
887     pid_t pid;
888     int i;
889     int seq_len;
890     int ret;
891     cpuset_t cpu_set;
892     PyObject *py_cpu_set;
893     PyObject *py_cpu_seq = NULL;
894 
895     if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &py_cpu_set))
896         return NULL;
897 
898     py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer");
899     if (!py_cpu_seq)
900         return NULL;
901     seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);
902 
903     // calculate the mask
904     CPU_ZERO(&cpu_set);
905     for (i = 0; i < seq_len; i++) {
906         PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);
907 #if PY_MAJOR_VERSION >= 3
908         long value = PyLong_AsLong(item);
909 #else
910         long value = PyInt_AsLong(item);
911 #endif
912         if (value == -1 || PyErr_Occurred())
913             goto error;
914         CPU_SET(value, &cpu_set);
915     }
916 
917     // set affinity
918     ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, pid,
919                              sizeof(cpu_set), &cpu_set);
920     if (ret != 0) {
921         PyErr_SetFromErrno(PyExc_OSError);
922         goto error;
923     }
924 
925     Py_DECREF(py_cpu_seq);
926     Py_RETURN_NONE;
927 
928 error:
929     if (py_cpu_seq != NULL)
930         Py_DECREF(py_cpu_seq);
931     return NULL;
932 }
933 
934 
935 PyObject *
psutil_cpu_stats(PyObject * self,PyObject * args)936 psutil_cpu_stats(PyObject *self, PyObject *args) {
937     unsigned int v_soft;
938     unsigned int v_intr;
939     unsigned int v_syscall;
940     unsigned int v_trap;
941     unsigned int v_swtch;
942     size_t size = sizeof(v_soft);
943 
944     if (sysctlbyname("vm.stats.sys.v_soft", &v_soft, &size, NULL, 0)) {
945         return PyErr_SetFromOSErrnoWithSyscall(
946             "sysctlbyname('vm.stats.sys.v_soft')");
947     }
948     if (sysctlbyname("vm.stats.sys.v_intr", &v_intr, &size, NULL, 0)) {
949         return PyErr_SetFromOSErrnoWithSyscall(
950             "sysctlbyname('vm.stats.sys.v_intr')");
951     }
952     if (sysctlbyname("vm.stats.sys.v_syscall", &v_syscall, &size, NULL, 0)) {
953         return PyErr_SetFromOSErrnoWithSyscall(
954             "sysctlbyname('vm.stats.sys.v_syscall')");
955     }
956     if (sysctlbyname("vm.stats.sys.v_trap", &v_trap, &size, NULL, 0)) {
957         return PyErr_SetFromOSErrnoWithSyscall(
958             "sysctlbyname('vm.stats.sys.v_trap')");
959     }
960     if (sysctlbyname("vm.stats.sys.v_swtch", &v_swtch, &size, NULL, 0)) {
961         return PyErr_SetFromOSErrnoWithSyscall(
962             "sysctlbyname('vm.stats.sys.v_swtch')");
963     }
964 
965     return Py_BuildValue(
966         "IIIII",
967         v_swtch,  // ctx switches
968         v_intr,  // interrupts
969         v_soft,  // software interrupts
970         v_syscall,  // syscalls
971         v_trap  // traps
972     );
973 }
974 
975 
976 /*
977  * Return battery information.
978  */
979 PyObject *
psutil_sensors_battery(PyObject * self,PyObject * args)980 psutil_sensors_battery(PyObject *self, PyObject *args) {
981     int percent;
982     int minsleft;
983     int power_plugged;
984     size_t size = sizeof(percent);
985 
986     if (sysctlbyname("hw.acpi.battery.life", &percent, &size, NULL, 0))
987         goto error;
988     if (sysctlbyname("hw.acpi.battery.time", &minsleft, &size, NULL, 0))
989         goto error;
990     if (sysctlbyname("hw.acpi.acline", &power_plugged, &size, NULL, 0))
991         goto error;
992     return Py_BuildValue("iii", percent, minsleft, power_plugged);
993 
994 error:
995     // see: https://github.com/giampaolo/psutil/issues/1074
996     if (errno == ENOENT)
997         PyErr_SetString(PyExc_NotImplementedError, "no battery");
998     else
999         PyErr_SetFromErrno(PyExc_OSError);
1000     return NULL;
1001 }
1002 
1003 
1004 /*
1005  * Return temperature information for a given CPU core number.
1006  */
1007 PyObject *
psutil_sensors_cpu_temperature(PyObject * self,PyObject * args)1008 psutil_sensors_cpu_temperature(PyObject *self, PyObject *args) {
1009     int current;
1010     int tjmax;
1011     int core;
1012     char sensor[26];
1013     size_t size = sizeof(current);
1014 
1015     if (! PyArg_ParseTuple(args, "i", &core))
1016         return NULL;
1017     sprintf(sensor, "dev.cpu.%d.temperature", core);
1018     if (sysctlbyname(sensor, &current, &size, NULL, 0))
1019         goto error;
1020     current = DECIKELVIN_2_CELCIUS(current);
1021 
1022     // Return -273 in case of faliure.
1023     sprintf(sensor, "dev.cpu.%d.coretemp.tjmax", core);
1024     if (sysctlbyname(sensor, &tjmax, &size, NULL, 0))
1025         tjmax = 0;
1026     tjmax = DECIKELVIN_2_CELCIUS(tjmax);
1027 
1028     return Py_BuildValue("ii", current, tjmax);
1029 
1030 error:
1031     if (errno == ENOENT)
1032         PyErr_SetString(PyExc_NotImplementedError, "no temperature sensors");
1033     else
1034         PyErr_SetFromErrno(PyExc_OSError);
1035     return NULL;
1036 }
1037 
1038 
1039 /*
1040  * Return frequency information of a given CPU.
1041  * As of Dec 2018 only CPU 0 appears to be supported and all other
1042  * cores match the frequency of CPU 0.
1043  */
1044 PyObject *
psutil_cpu_freq(PyObject * self,PyObject * args)1045 psutil_cpu_freq(PyObject *self, PyObject *args) {
1046     int current;
1047     int core;
1048     char sensor[26];
1049     char available_freq_levels[1000];
1050     size_t size = sizeof(current);
1051 
1052     if (! PyArg_ParseTuple(args, "i", &core))
1053         return NULL;
1054     // https://www.unix.com/man-page/FreeBSD/4/cpufreq/
1055     sprintf(sensor, "dev.cpu.%d.freq", core);
1056     if (sysctlbyname(sensor, &current, &size, NULL, 0))
1057         goto error;
1058 
1059     size = sizeof(available_freq_levels);
1060     // https://www.unix.com/man-page/FreeBSD/4/cpufreq/
1061     // In case of failure, an empty string is returned.
1062     sprintf(sensor, "dev.cpu.%d.freq_levels", core);
1063     sysctlbyname(sensor, &available_freq_levels, &size, NULL, 0);
1064 
1065     return Py_BuildValue("is", current, available_freq_levels);
1066 
1067 error:
1068     if (errno == ENOENT)
1069         PyErr_SetString(PyExc_NotImplementedError, "unable to read frequency");
1070     else
1071         PyErr_SetFromErrno(PyExc_OSError);
1072     return NULL;
1073 }
1074 
1075 
1076 /*
1077  * An emulation of Linux prlimit(). Returns a (soft, hard) tuple.
1078  */
1079 PyObject *
psutil_proc_getrlimit(PyObject * self,PyObject * args)1080 psutil_proc_getrlimit(PyObject *self, PyObject *args) {
1081     pid_t pid;
1082     int ret;
1083     int resource;
1084     size_t len;
1085     int name[5];
1086     struct rlimit rlp;
1087 
1088     if (! PyArg_ParseTuple(args, _Py_PARSE_PID "i", &pid, &resource))
1089         return NULL;
1090 
1091     name[0] = CTL_KERN;
1092     name[1] = KERN_PROC;
1093     name[2] = KERN_PROC_RLIMIT;
1094     name[3] = pid;
1095     name[4] = resource;
1096     len = sizeof(rlp);
1097 
1098     ret = sysctl(name, 5, &rlp, &len, NULL, 0);
1099     if (ret == -1)
1100         return PyErr_SetFromErrno(PyExc_OSError);
1101 
1102 #if defined(HAVE_LONG_LONG)
1103     return Py_BuildValue("LL",
1104                          (PY_LONG_LONG) rlp.rlim_cur,
1105                          (PY_LONG_LONG) rlp.rlim_max);
1106 #else
1107     return Py_BuildValue("ll",
1108                          (long) rlp.rlim_cur,
1109                          (long) rlp.rlim_max);
1110 #endif
1111 }
1112 
1113 
1114 /*
1115  * An emulation of Linux prlimit() (set).
1116  */
1117 PyObject *
psutil_proc_setrlimit(PyObject * self,PyObject * args)1118 psutil_proc_setrlimit(PyObject *self, PyObject *args) {
1119     pid_t pid;
1120     int ret;
1121     int resource;
1122     int name[5];
1123     struct rlimit new;
1124     struct rlimit *newp = NULL;
1125     PyObject *py_soft = NULL;
1126     PyObject *py_hard = NULL;
1127 
1128     if (! PyArg_ParseTuple(
1129             args, _Py_PARSE_PID "iOO", &pid, &resource, &py_soft, &py_hard))
1130         return NULL;
1131 
1132     name[0] = CTL_KERN;
1133     name[1] = KERN_PROC;
1134     name[2] = KERN_PROC_RLIMIT;
1135     name[3] = pid;
1136     name[4] = resource;
1137 
1138 #if defined(HAVE_LONG_LONG)
1139     new.rlim_cur = PyLong_AsLongLong(py_soft);
1140     if (new.rlim_cur == (rlim_t) - 1 && PyErr_Occurred())
1141         return NULL;
1142     new.rlim_max = PyLong_AsLongLong(py_hard);
1143     if (new.rlim_max == (rlim_t) - 1 && PyErr_Occurred())
1144         return NULL;
1145 #else
1146     new.rlim_cur = PyLong_AsLong(py_soft);
1147     if (new.rlim_cur == (rlim_t) - 1 && PyErr_Occurred())
1148         return NULL;
1149     new.rlim_max = PyLong_AsLong(py_hard);
1150     if (new.rlim_max == (rlim_t) - 1 && PyErr_Occurred())
1151         return NULL;
1152 #endif
1153     newp = &new;
1154     ret = sysctl(name, 5, NULL, 0, newp, sizeof(*newp));
1155     if (ret == -1)
1156         return PyErr_SetFromErrno(PyExc_OSError);
1157     Py_RETURN_NONE;
1158 }
1159