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, ¤t, &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, ¤t, &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