1 /*
2 * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil.
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 *
7 * Platform-specific module methods for OpenBSD.
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 <fcntl.h>
17 #include <sys/types.h>
18 #include <sys/param.h>
19 #include <sys/sysctl.h>
20 #include <sys/user.h>
21 #include <sys/proc.h>
22 #include <sys/mount.h> // for VFS_*
23 #include <sys/swap.h> // for swap_mem
24 #include <sys/vmmeter.h> // for vmtotal struct
25 #include <signal.h>
26 #include <kvm.h>
27 // connection stuff
28 #include <netdb.h> // for NI_MAXHOST
29 #include <sys/socket.h>
30 #include <sys/sched.h> // for CPUSTATES & CP_*
31 #define _KERNEL // for DTYPE_*
32 #include <sys/file.h>
33 #undef _KERNEL
34 #include <sys/disk.h> // struct diskstats
35 #include <arpa/inet.h> // for inet_ntoa()
36 #include <err.h> // for warn() & err()
37
38 #include "../../_psutil_common.h"
39 #include "../../_psutil_posix.h"
40
41 #define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
42 // #define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
43
44
45 // ============================================================================
46 // Utility functions
47 // ============================================================================
48
49 int
psutil_kinfo_proc(pid_t pid,struct kinfo_proc * proc)50 psutil_kinfo_proc(pid_t pid, struct kinfo_proc *proc) {
51 // Fills a kinfo_proc struct based on process pid.
52 int ret;
53 int mib[6];
54 size_t size = sizeof(struct kinfo_proc);
55
56 mib[0] = CTL_KERN;
57 mib[1] = KERN_PROC;
58 mib[2] = KERN_PROC_PID;
59 mib[3] = pid;
60 mib[4] = size;
61 mib[5] = 1;
62
63 ret = sysctl((int*)mib, 6, proc, &size, NULL, 0);
64 if (ret == -1) {
65 PyErr_SetFromErrno(PyExc_OSError);
66 return -1;
67 }
68 // sysctl stores 0 in the size if we can't find the process information.
69 if (size == 0) {
70 NoSuchProcess("");
71 return -1;
72 }
73 return 0;
74 }
75
76
77 struct kinfo_file *
kinfo_getfile(long pid,int * cnt)78 kinfo_getfile(long pid, int* cnt) {
79 // Mimic's FreeBSD kinfo_file call, taking a pid and a ptr to an
80 // int as arg and returns an array with cnt struct kinfo_file.
81 int mib[6];
82 size_t len;
83 struct kinfo_file* kf;
84 mib[0] = CTL_KERN;
85 mib[1] = KERN_FILE;
86 mib[2] = KERN_FILE_BYPID;
87 mib[3] = (int) pid;
88 mib[4] = sizeof(struct kinfo_file);
89 mib[5] = 0;
90
91 /* get the size of what would be returned */
92 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
93 PyErr_SetFromErrno(PyExc_OSError);
94 return NULL;
95 }
96 if ((kf = malloc(len)) == NULL) {
97 PyErr_NoMemory();
98 return NULL;
99 }
100 mib[5] = (int)(len / sizeof(struct kinfo_file));
101 if (sysctl(mib, 6, kf, &len, NULL, 0) < 0) {
102 free(kf);
103 PyErr_SetFromErrno(PyExc_OSError);
104 return NULL;
105 }
106
107 *cnt = (int)(len / sizeof(struct kinfo_file));
108 return kf;
109 }
110
111
112 // ============================================================================
113 // APIS
114 // ============================================================================
115
116 int
psutil_get_proc_list(struct kinfo_proc ** procList,size_t * procCount)117 psutil_get_proc_list(struct kinfo_proc **procList, size_t *procCount) {
118 // Returns a list of all BSD processes on the system. This routine
119 // allocates the list and puts it in *procList and a count of the
120 // number of entries in *procCount. You are responsible for freeing
121 // this list (use "free" from System framework).
122 // On success, the function returns 0.
123 // On error, the function returns a BSD errno value.
124 struct kinfo_proc *result;
125 // Declaring name as const requires us to cast it when passing it to
126 // sysctl because the prototype doesn't include the const modifier.
127 char errbuf[_POSIX2_LINE_MAX];
128 int cnt;
129 kvm_t *kd;
130
131 assert(procList != NULL);
132 assert(*procList == NULL);
133 assert(procCount != NULL);
134
135 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
136
137 if (kd == NULL) {
138 return errno;
139 }
140
141 result = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &cnt);
142 if (result == NULL) {
143 kvm_close(kd);
144 err(1, NULL);
145 return errno;
146 }
147
148 *procCount = (size_t)cnt;
149
150 size_t mlen = cnt * sizeof(struct kinfo_proc);
151
152 if ((*procList = malloc(mlen)) == NULL) {
153 kvm_close(kd);
154 err(1, NULL);
155 return errno;
156 }
157
158 memcpy(*procList, result, mlen);
159 assert(*procList != NULL);
160 kvm_close(kd);
161
162 return 0;
163 }
164
165
166 char **
_psutil_get_argv(long pid)167 _psutil_get_argv(long pid) {
168 static char **argv;
169 int argv_mib[] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV};
170 size_t argv_size = 128;
171 // Loop and reallocate until we have enough space to fit argv.
172 for (;; argv_size *= 2) {
173 if (argv_size >= 8192) {
174 PyErr_SetString(PyExc_RuntimeError,
175 "can't allocate enough space for KERN_PROC_ARGV");
176 return NULL;
177 }
178 if ((argv = realloc(argv, argv_size)) == NULL)
179 continue;
180 if (sysctl(argv_mib, 4, argv, &argv_size, NULL, 0) == 0)
181 return argv;
182 if (errno == ENOMEM)
183 continue;
184 PyErr_SetFromErrno(PyExc_OSError);
185 return NULL;
186 }
187 }
188
189
190 // returns the command line as a python list object
191 PyObject *
psutil_get_cmdline(long pid)192 psutil_get_cmdline(long pid) {
193 static char **argv;
194 char **p;
195 PyObject *py_arg = NULL;
196 PyObject *py_retlist = Py_BuildValue("[]");
197
198 if (!py_retlist)
199 return NULL;
200 if (pid < 0)
201 return py_retlist;
202
203 if ((argv = _psutil_get_argv(pid)) == NULL)
204 goto error;
205
206 for (p = argv; *p != NULL; p++) {
207 py_arg = PyUnicode_DecodeFSDefault(*p);
208 if (!py_arg)
209 goto error;
210 if (PyList_Append(py_retlist, py_arg))
211 goto error;
212 Py_DECREF(py_arg);
213 }
214 return py_retlist;
215
216 error:
217 Py_XDECREF(py_arg);
218 Py_DECREF(py_retlist);
219 return NULL;
220 }
221
222
223 PyObject *
psutil_proc_threads(PyObject * self,PyObject * args)224 psutil_proc_threads(PyObject *self, PyObject *args) {
225 // OpenBSD reference:
226 // https://github.com/janmojzis/pstree/blob/master/proc_kvm.c
227 // Note: this requires root access, else it will fail trying
228 // to access /dev/kmem.
229 long pid;
230 kvm_t *kd = NULL;
231 int nentries, i;
232 char errbuf[4096];
233 struct kinfo_proc *kp;
234 PyObject *py_retlist = PyList_New(0);
235 PyObject *py_tuple = NULL;
236
237 if (py_retlist == NULL)
238 return NULL;
239 if (! PyArg_ParseTuple(args, "l", &pid))
240 goto error;
241
242 kd = kvm_openfiles(0, 0, 0, O_RDONLY, errbuf);
243 if (! kd) {
244 if (strstr(errbuf, "Permission denied") != NULL)
245 AccessDenied("");
246 else
247 PyErr_Format(PyExc_RuntimeError, "kvm_openfiles() syscall failed");
248 goto error;
249 }
250
251 kp = kvm_getprocs(
252 kd, KERN_PROC_PID | KERN_PROC_SHOW_THREADS | KERN_PROC_KTHREAD, pid,
253 sizeof(*kp), &nentries);
254 if (! kp) {
255 if (strstr(errbuf, "Permission denied") != NULL)
256 AccessDenied("");
257 else
258 PyErr_Format(PyExc_RuntimeError, "kvm_getprocs() syscall failed");
259 goto error;
260 }
261
262 for (i = 0; i < nentries; i++) {
263 if (kp[i].p_tid < 0)
264 continue;
265 if (kp[i].p_pid == pid) {
266 py_tuple = Py_BuildValue(
267 "Idd",
268 kp[i].p_tid,
269 PSUTIL_KPT2DOUBLE(kp[i].p_uutime),
270 PSUTIL_KPT2DOUBLE(kp[i].p_ustime));
271 if (py_tuple == NULL)
272 goto error;
273 if (PyList_Append(py_retlist, py_tuple))
274 goto error;
275 Py_DECREF(py_tuple);
276 }
277 }
278
279 kvm_close(kd);
280 return py_retlist;
281
282 error:
283 Py_XDECREF(py_tuple);
284 Py_DECREF(py_retlist);
285 if (kd != NULL)
286 kvm_close(kd);
287 return NULL;
288 }
289
290
291 PyObject *
psutil_virtual_mem(PyObject * self,PyObject * args)292 psutil_virtual_mem(PyObject *self, PyObject *args) {
293 int64_t total_physmem;
294 int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
295 int bcstats_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT};
296 int physmem_mib[] = {CTL_HW, HW_PHYSMEM64};
297 int vmmeter_mib[] = {CTL_VM, VM_METER};
298 size_t size;
299 struct uvmexp uvmexp;
300 struct bcachestats bcstats;
301 struct vmtotal vmdata;
302 long pagesize = getpagesize();
303
304 size = sizeof(total_physmem);
305 if (sysctl(physmem_mib, 2, &total_physmem, &size, NULL, 0) < 0) {
306 PyErr_SetFromErrno(PyExc_OSError);
307 return NULL;
308 }
309
310 size = sizeof(uvmexp);
311 if (sysctl(uvmexp_mib, 2, &uvmexp, &size, NULL, 0) < 0) {
312 PyErr_SetFromErrno(PyExc_OSError);
313 return NULL;
314 }
315
316 size = sizeof(bcstats);
317 if (sysctl(bcstats_mib, 3, &bcstats, &size, NULL, 0) < 0) {
318 PyErr_SetFromErrno(PyExc_OSError);
319 return NULL;
320 }
321
322 size = sizeof(vmdata);
323 if (sysctl(vmmeter_mib, 2, &vmdata, &size, NULL, 0) < 0) {
324 PyErr_SetFromErrno(PyExc_OSError);
325 return NULL;
326 }
327
328 return Py_BuildValue("KKKKKKKK",
329 // Note: many programs calculate total memory as
330 // "uvmexp.npages * pagesize" but this is incorrect and does not
331 // match "sysctl | grep hw.physmem".
332 (unsigned long long) total_physmem,
333 (unsigned long long) uvmexp.free * pagesize,
334 (unsigned long long) uvmexp.active * pagesize,
335 (unsigned long long) uvmexp.inactive * pagesize,
336 (unsigned long long) uvmexp.wired * pagesize,
337 // this is how "top" determines it
338 (unsigned long long) bcstats.numbufpages * pagesize, // cached
339 (unsigned long long) 0, // buffers
340 (unsigned long long) vmdata.t_vmshr + vmdata.t_rmshr // shared
341 );
342 }
343
344
345 PyObject *
psutil_swap_mem(PyObject * self,PyObject * args)346 psutil_swap_mem(PyObject *self, PyObject *args) {
347 uint64_t swap_total, swap_free;
348 struct swapent *swdev;
349 int nswap, i;
350
351 if ((nswap = swapctl(SWAP_NSWAP, 0, 0)) == 0) {
352 PyErr_SetFromErrno(PyExc_OSError);
353 return NULL;
354 }
355
356 if ((swdev = calloc(nswap, sizeof(*swdev))) == NULL) {
357 PyErr_NoMemory();
358 return NULL;
359 }
360
361 if (swapctl(SWAP_STATS, swdev, nswap) == -1) {
362 PyErr_SetFromErrno(PyExc_OSError);
363 goto error;
364 }
365
366 // Total things up.
367 swap_total = swap_free = 0;
368 for (i = 0; i < nswap; i++) {
369 if (swdev[i].se_flags & SWF_ENABLE) {
370 swap_free += (swdev[i].se_nblks - swdev[i].se_inuse);
371 swap_total += swdev[i].se_nblks;
372 }
373 }
374
375 free(swdev);
376 return Py_BuildValue("(LLLII)",
377 swap_total * DEV_BSIZE,
378 (swap_total - swap_free) * DEV_BSIZE,
379 swap_free * DEV_BSIZE,
380 // swap in / swap out is not supported as the
381 // swapent struct does not provide any info
382 // about it.
383 0, 0);
384
385 error:
386 free(swdev);
387 return NULL;
388 }
389
390
391 PyObject *
psutil_proc_num_fds(PyObject * self,PyObject * args)392 psutil_proc_num_fds(PyObject *self, PyObject *args) {
393 long pid;
394 int cnt;
395
396 struct kinfo_file *freep;
397 struct kinfo_proc kipp;
398
399 if (! PyArg_ParseTuple(args, "l", &pid))
400 return NULL;
401 if (psutil_kinfo_proc(pid, &kipp) == -1)
402 return NULL;
403
404 errno = 0;
405 freep = kinfo_getfile(pid, &cnt);
406 if (freep == NULL) {
407 psutil_raise_for_pid(pid, "kinfo_getfile()");
408 return NULL;
409 }
410 free(freep);
411
412 return Py_BuildValue("i", cnt);
413 }
414
415
416 PyObject *
psutil_proc_cwd(PyObject * self,PyObject * args)417 psutil_proc_cwd(PyObject *self, PyObject *args) {
418 // Reference:
419 // https://github.com/openbsd/src/blob/
420 // 588f7f8c69786211f2d16865c552afb91b1c7cba/bin/ps/print.c#L191
421 long pid;
422 struct kinfo_proc kp;
423 char path[MAXPATHLEN];
424 size_t pathlen = sizeof path;
425
426 if (! PyArg_ParseTuple(args, "l", &pid))
427 return NULL;
428 if (psutil_kinfo_proc(pid, &kp) == -1)
429 return NULL;
430
431 int name[] = { CTL_KERN, KERN_PROC_CWD, pid };
432 if (sysctl(name, 3, path, &pathlen, NULL, 0) != 0) {
433 PyErr_SetFromErrno(PyExc_OSError);
434 return NULL;
435 }
436 return PyUnicode_DecodeFSDefault(path);
437 }
438
439
440 // see sys/kern/kern_sysctl.c lines 1100 and
441 // usr.bin/fstat/fstat.c print_inet_details()
442 static char *
psutil_convert_ipv4(int family,uint32_t addr[4])443 psutil_convert_ipv4(int family, uint32_t addr[4]) {
444 struct in_addr a;
445 memcpy(&a, addr, sizeof(a));
446 return inet_ntoa(a);
447 }
448
449
450 static char *
psutil_inet6_addrstr(struct in6_addr * p)451 psutil_inet6_addrstr(struct in6_addr *p)
452 {
453 struct sockaddr_in6 sin6;
454 static char hbuf[NI_MAXHOST];
455 const int niflags = NI_NUMERICHOST;
456
457 memset(&sin6, 0, sizeof(sin6));
458 sin6.sin6_family = AF_INET6;
459 sin6.sin6_len = sizeof(struct sockaddr_in6);
460 sin6.sin6_addr = *p;
461 if (IN6_IS_ADDR_LINKLOCAL(p) &&
462 *(u_int16_t *)&sin6.sin6_addr.s6_addr[2] != 0) {
463 sin6.sin6_scope_id =
464 ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
465 sin6.sin6_addr.s6_addr[2] = sin6.sin6_addr.s6_addr[3] = 0;
466 }
467
468 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
469 hbuf, sizeof(hbuf), NULL, 0, niflags))
470 return "invalid";
471
472 return hbuf;
473 }
474
475
476 /*
477 * List process connections.
478 * Note: there is no net_connections() on OpenBSD. The Python
479 * implementation will iterate over all processes and use this
480 * function.
481 * Note: local and remote paths cannot be determined for UNIX sockets.
482 */
483 PyObject *
psutil_proc_connections(PyObject * self,PyObject * args)484 psutil_proc_connections(PyObject *self, PyObject *args) {
485 long pid;
486 int i;
487 int cnt;
488 struct kinfo_file *freep = NULL;
489 struct kinfo_file *kif;
490 char *tcplist = NULL;
491 PyObject *py_retlist = PyList_New(0);
492 PyObject *py_tuple = NULL;
493 PyObject *py_laddr = NULL;
494 PyObject *py_raddr = NULL;
495 PyObject *py_af_filter = NULL;
496 PyObject *py_type_filter = NULL;
497 PyObject *py_family = NULL;
498 PyObject *_type = NULL;
499
500 if (py_retlist == NULL)
501 return NULL;
502 if (! PyArg_ParseTuple(args, "lOO", &pid, &py_af_filter, &py_type_filter))
503 goto error;
504 if (!PySequence_Check(py_af_filter) || !PySequence_Check(py_type_filter)) {
505 PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
506 goto error;
507 }
508
509 errno = 0;
510 freep = kinfo_getfile(pid, &cnt);
511 if (freep == NULL) {
512 psutil_raise_for_pid(pid, "kinfo_getfile()");
513 goto error;
514 }
515
516 for (i = 0; i < cnt; i++) {
517 int state;
518 int lport;
519 int rport;
520 char addrbuf[NI_MAXHOST + 2];
521 int inseq;
522 struct in6_addr laddr6;
523 py_tuple = NULL;
524 py_laddr = NULL;
525 py_raddr = NULL;
526
527 kif = &freep[i];
528 if (kif->f_type == DTYPE_SOCKET) {
529 // apply filters
530 py_family = PyLong_FromLong((long)kif->so_family);
531 inseq = PySequence_Contains(py_af_filter, py_family);
532 Py_DECREF(py_family);
533 if (inseq == 0)
534 continue;
535 _type = PyLong_FromLong((long)kif->so_type);
536 inseq = PySequence_Contains(py_type_filter, _type);
537 Py_DECREF(_type);
538 if (inseq == 0)
539 continue;
540
541 // IPv4 / IPv6 socket
542 if ((kif->so_family == AF_INET) || (kif->so_family == AF_INET6)) {
543 // fill status
544 if (kif->so_type == SOCK_STREAM)
545 state = kif->t_state;
546 else
547 state = PSUTIL_CONN_NONE;
548
549 // ports
550 lport = ntohs(kif->inp_lport);
551 rport = ntohs(kif->inp_fport);
552
553 // local address, IPv4
554 if (kif->so_family == AF_INET) {
555 py_laddr = Py_BuildValue(
556 "(si)",
557 psutil_convert_ipv4(kif->so_family, kif->inp_laddru),
558 lport);
559 if (!py_laddr)
560 goto error;
561 }
562 else {
563 // local address, IPv6
564 memcpy(&laddr6, kif->inp_laddru, sizeof(laddr6));
565 snprintf(addrbuf, sizeof(addrbuf), "%s",
566 psutil_inet6_addrstr(&laddr6));
567 py_laddr = Py_BuildValue("(si)", addrbuf, lport);
568 if (!py_laddr)
569 goto error;
570 }
571
572 if (rport != 0) {
573 // remote address, IPv4
574 if (kif->so_family == AF_INET) {
575 py_raddr = Py_BuildValue(
576 "(si)",
577 psutil_convert_ipv4(
578 kif->so_family, kif->inp_faddru),
579 rport);
580 }
581 else {
582 // remote address, IPv6
583 memcpy(&laddr6, kif->inp_faddru, sizeof(laddr6));
584 snprintf(addrbuf, sizeof(addrbuf), "%s",
585 psutil_inet6_addrstr(&laddr6));
586 py_raddr = Py_BuildValue("(si)", addrbuf, rport);
587 if (!py_raddr)
588 goto error;
589 }
590 }
591 else {
592 py_raddr = Py_BuildValue("()");
593 }
594
595 if (!py_raddr)
596 goto error;
597 py_tuple = Py_BuildValue(
598 "(iiiNNi)",
599 kif->fd_fd,
600 kif->so_family,
601 kif->so_type,
602 py_laddr,
603 py_raddr,
604 state);
605 if (!py_tuple)
606 goto error;
607 if (PyList_Append(py_retlist, py_tuple))
608 goto error;
609 Py_DECREF(py_tuple);
610 }
611 // UNIX socket.
612 // XXX: local addr is supposed to be in "unp_path" but it
613 // always empty; also "fstat" command is not able to show
614 // UNIX socket paths.
615 else if (kif->so_family == AF_UNIX) {
616 py_tuple = Py_BuildValue(
617 "(iiissi)",
618 kif->fd_fd,
619 kif->so_family,
620 kif->so_type,
621 "", // laddr (kif->unp_path is empty)
622 "", // raddr
623 PSUTIL_CONN_NONE);
624 if (!py_tuple)
625 goto error;
626 if (PyList_Append(py_retlist, py_tuple))
627 goto error;
628 Py_DECREF(py_tuple);
629 Py_INCREF(Py_None);
630 }
631 }
632 }
633 free(freep);
634 free(tcplist);
635 return py_retlist;
636
637 error:
638 Py_XDECREF(py_tuple);
639 Py_XDECREF(py_laddr);
640 Py_XDECREF(py_raddr);
641 Py_DECREF(py_retlist);
642 if (freep != NULL)
643 free(freep);
644 if (tcplist != NULL)
645 free(tcplist);
646 return NULL;
647 }
648
649
650 PyObject *
psutil_per_cpu_times(PyObject * self,PyObject * args)651 psutil_per_cpu_times(PyObject *self, PyObject *args) {
652 int mib[3];
653 int ncpu;
654 size_t len;
655 size_t size;
656 int i;
657 PyObject *py_retlist = PyList_New(0);
658 PyObject *py_cputime = NULL;
659
660 if (py_retlist == NULL)
661 return NULL;
662
663
664 // retrieve the number of cpus
665 mib[0] = CTL_HW;
666 mib[1] = HW_NCPU;
667 len = sizeof(ncpu);
668 if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
669 PyErr_SetFromErrno(PyExc_OSError);
670 goto error;
671 }
672 uint64_t cpu_time[CPUSTATES];
673
674 for (i = 0; i < ncpu; i++) {
675 // per-cpu info
676 mib[0] = CTL_KERN;
677 mib[1] = KERN_CPTIME2;
678 mib[2] = i;
679 size = sizeof(cpu_time);
680 if (sysctl(mib, 3, &cpu_time, &size, NULL, 0) == -1) {
681 warn("failed to get kern.cptime2");
682 PyErr_SetFromErrno(PyExc_OSError);
683 return NULL;
684 }
685
686 py_cputime = Py_BuildValue(
687 "(ddddd)",
688 (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,
689 (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,
690 (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,
691 (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,
692 (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC);
693 if (!py_cputime)
694 goto error;
695 if (PyList_Append(py_retlist, py_cputime))
696 goto error;
697 Py_DECREF(py_cputime);
698 }
699
700 return py_retlist;
701
702 error:
703 Py_XDECREF(py_cputime);
704 Py_DECREF(py_retlist);
705 return NULL;
706 }
707
708
709 PyObject *
psutil_disk_io_counters(PyObject * self,PyObject * args)710 psutil_disk_io_counters(PyObject *self, PyObject *args) {
711 int i, dk_ndrive, mib[3];
712 size_t len;
713 struct diskstats *stats = NULL;
714
715 PyObject *py_retdict = PyDict_New();
716 PyObject *py_disk_info = NULL;
717 if (py_retdict == NULL)
718 return NULL;
719
720 mib[0] = CTL_HW;
721 mib[1] = HW_DISKSTATS;
722 len = 0;
723 if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) {
724 warn("can't get hw.diskstats size");
725 PyErr_SetFromErrno(PyExc_OSError);
726 goto error;
727 }
728 dk_ndrive = (int)(len / sizeof(struct diskstats));
729
730 stats = malloc(len);
731 if (stats == NULL) {
732 warn("can't malloc");
733 PyErr_NoMemory();
734 goto error;
735 }
736 if (sysctl(mib, 2, stats, &len, NULL, 0) < 0 ) {
737 warn("could not read hw.diskstats");
738 PyErr_SetFromErrno(PyExc_OSError);
739 goto error;
740 }
741
742 for (i = 0; i < dk_ndrive; i++) {
743 py_disk_info = Py_BuildValue(
744 "(KKKK)",
745 stats[i].ds_rxfer, // num reads
746 stats[i].ds_wxfer, // num writes
747 stats[i].ds_rbytes, // read bytes
748 stats[i].ds_wbytes // write bytes
749 );
750 if (!py_disk_info)
751 goto error;
752 if (PyDict_SetItemString(py_retdict, stats[i].ds_name, py_disk_info))
753 goto error;
754 Py_DECREF(py_disk_info);
755 }
756
757 free(stats);
758 return py_retdict;
759
760 error:
761 Py_XDECREF(py_disk_info);
762 Py_DECREF(py_retdict);
763 if (stats != NULL)
764 free(stats);
765 return NULL;
766 }
767
768
769 PyObject *
psutil_cpu_stats(PyObject * self,PyObject * args)770 psutil_cpu_stats(PyObject *self, PyObject *args) {
771 size_t size;
772 struct uvmexp uv;
773 int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
774
775 size = sizeof(uv);
776 if (sysctl(uvmexp_mib, 2, &uv, &size, NULL, 0) < 0) {
777 PyErr_SetFromErrno(PyExc_OSError);
778 return NULL;
779 }
780
781 return Py_BuildValue(
782 "IIIIIII",
783 uv.swtch, // ctx switches
784 uv.intrs, // interrupts - XXX always 0, will be determined via /proc
785 uv.softs, // soft interrupts
786 uv.syscalls, // syscalls - XXX always 0
787 uv.traps, // traps
788 uv.faults, // faults
789 uv.forks // forks
790 );
791 }
792