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