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