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 NetBSD.
8 */
9
10 #if defined(PSUTIL_NETBSD)
11 #define _KMEMUSER
12 #endif
13
14 #include <Python.h>
15 #include <assert.h>
16 #include <err.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <sys/types.h>
23 #include <sys/param.h>
24 #include <sys/sysctl.h>
25 #include <sys/proc.h>
26 #include <sys/swap.h> // for swap_mem
27 #include <signal.h>
28 #include <kvm.h>
29 // connection stuff
30 #include <netdb.h> // for NI_MAXHOST
31 #include <sys/socket.h>
32 #include <sys/sched.h> // for CPUSTATES & CP_*
33 #define _KERNEL // for DTYPE_*
34 #include <sys/file.h>
35 #undef _KERNEL
36 #include <sys/disk.h> // struct diskstats
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39
40 #include "../../_psutil_common.h"
41 #include "../../_psutil_posix.h"
42 #include "specific.h"
43
44
45 #define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
46 #define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
47
48
49 // ============================================================================
50 // Utility functions
51 // ============================================================================
52
53
54 int
psutil_kinfo_proc(pid_t pid,kinfo_proc * proc)55 psutil_kinfo_proc(pid_t pid, kinfo_proc *proc) {
56 // Fills a kinfo_proc struct based on process pid.
57 int ret;
58 int mib[6];
59 size_t size = sizeof(kinfo_proc);
60
61 mib[0] = CTL_KERN;
62 mib[1] = KERN_PROC2;
63 mib[2] = KERN_PROC_PID;
64 mib[3] = pid;
65 mib[4] = size;
66 mib[5] = 1;
67
68 ret = sysctl((int*)mib, 6, proc, &size, NULL, 0);
69 if (ret == -1) {
70 PyErr_SetFromErrno(PyExc_OSError);
71 return -1;
72 }
73 // sysctl stores 0 in the size if we can't find the process information.
74 if (size == 0) {
75 NoSuchProcess("sysctl (size = 0)");
76 return -1;
77 }
78 return 0;
79 }
80
81
82 struct kinfo_file *
kinfo_getfile(pid_t pid,int * cnt)83 kinfo_getfile(pid_t pid, int* cnt) {
84 // Mimic's FreeBSD kinfo_file call, taking a pid and a ptr to an
85 // int as arg and returns an array with cnt struct kinfo_file.
86 int mib[6];
87 size_t len;
88 struct kinfo_file* kf;
89 mib[0] = CTL_KERN;
90 mib[1] = KERN_FILE2;
91 mib[2] = KERN_FILE_BYPID;
92 mib[3] = (int) pid;
93 mib[4] = sizeof(struct kinfo_file);
94 mib[5] = 0;
95
96 // get the size of what would be returned
97 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
98 PyErr_SetFromErrno(PyExc_OSError);
99 return NULL;
100 }
101 if ((kf = malloc(len)) == NULL) {
102 PyErr_NoMemory();
103 return NULL;
104 }
105 mib[5] = (int)(len / sizeof(struct kinfo_file));
106 if (sysctl(mib, 6, kf, &len, NULL, 0) < 0) {
107 PyErr_SetFromErrno(PyExc_OSError);
108 return NULL;
109 }
110
111 *cnt = (int)(len / sizeof(struct kinfo_file));
112 return kf;
113 }
114
115 PyObject *
psutil_proc_cwd(PyObject * self,PyObject * args)116 psutil_proc_cwd(PyObject *self, PyObject *args) {
117 long pid;
118
119 char path[MAXPATHLEN];
120 size_t pathlen = sizeof path;
121
122 if (! PyArg_ParseTuple(args, "l", &pid))
123 return NULL;
124
125 #ifdef KERN_PROC_CWD
126 int name[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_CWD};
127 if (sysctl(name, 4, path, &pathlen, NULL, 0) != 0) {
128 if (errno == ENOENT)
129 NoSuchProcess("");
130 else
131 PyErr_SetFromErrno(PyExc_OSError);
132 return NULL;
133 }
134 #else
135 char *buf;
136 if (asprintf(&buf, "/proc/%d/cwd", (int)pid) < 0) {
137 PyErr_NoMemory();
138 return NULL;
139 }
140
141 ssize_t len = readlink(buf, path, sizeof(path) - 1);
142 free(buf);
143 if (len == -1) {
144 if (errno == ENOENT)
145 NoSuchProcess("readlink (ENOENT)");
146 else
147 PyErr_SetFromErrno(PyExc_OSError);
148 return NULL;
149 }
150 path[len] = '\0';
151 #endif
152
153 return PyUnicode_DecodeFSDefault(path);
154 }
155
156
157 // XXX: This is no longer used as per
158 // https://github.com/giampaolo/psutil/pull/557#issuecomment-171912820
159 // Current implementation uses /proc instead.
160 // Left here just in case.
161 /*
162 PyObject *
163 psutil_proc_exe(PyObject *self, PyObject *args) {
164 #if __NetBSD_Version__ >= 799000000
165 pid_t pid;
166 char pathname[MAXPATHLEN];
167 int error;
168 int mib[4];
169 int ret;
170 size_t size;
171
172 if (! PyArg_ParseTuple(args, "l", &pid))
173 return NULL;
174 if (pid == 0) {
175 // else returns ENOENT
176 return Py_BuildValue("s", "");
177 }
178
179 mib[0] = CTL_KERN;
180 mib[1] = KERN_PROC_ARGS;
181 mib[2] = pid;
182 mib[3] = KERN_PROC_PATHNAME;
183
184 size = sizeof(pathname);
185 error = sysctl(mib, 4, NULL, &size, NULL, 0);
186 if (error == -1) {
187 PyErr_SetFromErrno(PyExc_OSError);
188 return NULL;
189 }
190
191 error = sysctl(mib, 4, pathname, &size, NULL, 0);
192 if (error == -1) {
193 PyErr_SetFromErrno(PyExc_OSError);
194 return NULL;
195 }
196 if (size == 0 || strlen(pathname) == 0) {
197 ret = psutil_pid_exists(pid);
198 if (ret == -1)
199 return NULL;
200 else if (ret == 0)
201 return NoSuchProcess("psutil_pid_exists");
202 else
203 strcpy(pathname, "");
204 }
205
206 return PyUnicode_DecodeFSDefault(pathname);
207 #else
208 return Py_BuildValue("s", "");
209 #endif
210 }
211 */
212
213 PyObject *
psutil_proc_num_threads(PyObject * self,PyObject * args)214 psutil_proc_num_threads(PyObject *self, PyObject *args) {
215 // Return number of threads used by process as a Python integer.
216 long pid;
217 kinfo_proc kp;
218 if (! PyArg_ParseTuple(args, "l", &pid))
219 return NULL;
220 if (psutil_kinfo_proc(pid, &kp) == -1)
221 return NULL;
222 return Py_BuildValue("l", (long)kp.p_nlwps);
223 }
224
225 PyObject *
psutil_proc_threads(PyObject * self,PyObject * args)226 psutil_proc_threads(PyObject *self, PyObject *args) {
227 pid_t pid;
228 int mib[5];
229 int i, nlwps;
230 ssize_t st;
231 size_t size;
232 struct kinfo_lwp *kl = NULL;
233 PyObject *py_retlist = PyList_New(0);
234 PyObject *py_tuple = NULL;
235
236 if (py_retlist == NULL)
237 return NULL;
238 if (! PyArg_ParseTuple(args, "l", &pid))
239 goto error;
240
241 mib[0] = CTL_KERN;
242 mib[1] = KERN_LWP;
243 mib[2] = pid;
244 mib[3] = sizeof(struct kinfo_lwp);
245 mib[4] = 0;
246
247 st = sysctl(mib, 5, NULL, &size, NULL, 0);
248 if (st == -1) {
249 PyErr_SetFromErrno(PyExc_OSError);
250 goto error;
251 }
252 if (size == 0) {
253 NoSuchProcess("sysctl (size = 0)");
254 goto error;
255 }
256
257 mib[4] = size / sizeof(size_t);
258 kl = malloc(size);
259 if (kl == NULL) {
260 PyErr_NoMemory();
261 goto error;
262 }
263
264 st = sysctl(mib, 5, kl, &size, NULL, 0);
265 if (st == -1) {
266 PyErr_SetFromErrno(PyExc_OSError);
267 goto error;
268 }
269 if (size == 0) {
270 NoSuchProcess("sysctl (size = 0)");
271 goto error;
272 }
273
274 nlwps = (int)(size / sizeof(struct kinfo_lwp));
275 for (i = 0; i < nlwps; i++) {
276 py_tuple = Py_BuildValue("idd",
277 (&kl[i])->l_lid,
278 PSUTIL_KPT2DOUBLE((&kl[i])->l_rtime),
279 PSUTIL_KPT2DOUBLE((&kl[i])->l_rtime));
280 if (py_tuple == NULL)
281 goto error;
282 if (PyList_Append(py_retlist, py_tuple))
283 goto error;
284 Py_DECREF(py_tuple);
285 }
286 free(kl);
287 return py_retlist;
288
289 error:
290 Py_XDECREF(py_tuple);
291 Py_DECREF(py_retlist);
292 if (kl != NULL)
293 free(kl);
294 return NULL;
295 }
296
297
298 // ============================================================================
299 // APIS
300 // ============================================================================
301
302 int
psutil_get_proc_list(kinfo_proc ** procList,size_t * procCount)303 psutil_get_proc_list(kinfo_proc **procList, size_t *procCount) {
304 // Returns a list of all BSD processes on the system. This routine
305 // allocates the list and puts it in *procList and a count of the
306 // number of entries in *procCount. You are responsible for freeing
307 // this list (use "free" from System framework).
308 // On success, the function returns 0.
309 // On error, the function returns a BSD errno value.
310 kinfo_proc *result;
311 // Declaring name as const requires us to cast it when passing it to
312 // sysctl because the prototype doesn't include the const modifier.
313 char errbuf[_POSIX2_LINE_MAX];
314 int cnt;
315 kvm_t *kd;
316
317 assert( procList != NULL);
318 assert(*procList == NULL);
319 assert(procCount != NULL);
320
321 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
322
323 if (kd == NULL) {
324 PyErr_Format(
325 PyExc_RuntimeError, "kvm_openfiles() syscall failed: %s", errbuf);
326 return 1;
327 }
328
329 result = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(kinfo_proc), &cnt);
330 if (result == NULL) {
331 PyErr_Format(PyExc_RuntimeError, "kvm_getproc2() syscall failed");
332 kvm_close(kd);
333 return 1;
334 }
335
336 *procCount = (size_t)cnt;
337
338 size_t mlen = cnt * sizeof(kinfo_proc);
339
340 if ((*procList = malloc(mlen)) == NULL) {
341 PyErr_NoMemory();
342 kvm_close(kd);
343 return 1;
344 }
345
346 memcpy(*procList, result, mlen);
347 assert(*procList != NULL);
348 kvm_close(kd);
349
350 return 0;
351 }
352
353
354 char *
psutil_get_cmd_args(pid_t pid,size_t * argsize)355 psutil_get_cmd_args(pid_t pid, size_t *argsize) {
356 int mib[4];
357 int st;
358 size_t len;
359 char *procargs;
360
361 mib[0] = CTL_KERN;
362 mib[1] = KERN_PROC_ARGS;
363 mib[2] = pid;
364 mib[3] = KERN_PROC_ARGV;
365 len = 0;
366
367 st = sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0);
368 if (st == -1) {
369 PyErr_SetFromErrno(PyExc_OSError);
370 return NULL;
371 }
372
373 procargs = (char *)malloc(len);
374 if (procargs == NULL) {
375 PyErr_NoMemory();
376 return NULL;
377 }
378 st = sysctl(mib, __arraycount(mib), procargs, &len, NULL, 0);
379 if (st == -1) {
380 free(procargs);
381 PyErr_SetFromErrno(PyExc_OSError);
382 return NULL;
383 }
384
385 *argsize = len;
386 return procargs;
387 }
388
389
390 // Return the command line as a python list object.
391 // XXX - most of the times sysctl() returns a truncated string.
392 // Also /proc/pid/cmdline behaves the same so it looks like this
393 // is a kernel bug.
394 PyObject *
psutil_get_cmdline(pid_t pid)395 psutil_get_cmdline(pid_t pid) {
396 char *argstr = NULL;
397 size_t pos = 0;
398 size_t argsize = 0;
399 PyObject *py_arg = NULL;
400 PyObject *py_retlist = PyList_New(0);
401
402 if (py_retlist == NULL)
403 return NULL;
404 if (pid == 0)
405 return py_retlist;
406
407 argstr = psutil_get_cmd_args(pid, &argsize);
408 if (argstr == NULL)
409 goto error;
410
411 // args are returned as a flattened string with \0 separators between
412 // arguments add each string to the list then step forward to the next
413 // separator
414 if (argsize > 0) {
415 while (pos < argsize) {
416 py_arg = PyUnicode_DecodeFSDefault(&argstr[pos]);
417 if (!py_arg)
418 goto error;
419 if (PyList_Append(py_retlist, py_arg))
420 goto error;
421 Py_DECREF(py_arg);
422 pos = pos + strlen(&argstr[pos]) + 1;
423 }
424 }
425
426 free(argstr);
427 return py_retlist;
428
429 error:
430 Py_XDECREF(py_arg);
431 Py_DECREF(py_retlist);
432 if (argstr != NULL)
433 free(argstr);
434 return NULL;
435 }
436
437
438 /*
439 * Virtual memory stats, taken from:
440 * https://github.com/satterly/zabbix-stats/blob/master/src/libs/zbxsysinfo/
441 * netbsd/memory.c
442 */
443 PyObject *
psutil_virtual_mem(PyObject * self,PyObject * args)444 psutil_virtual_mem(PyObject *self, PyObject *args) {
445 size_t size;
446 struct uvmexp_sysctl uv;
447 int mib[] = {CTL_VM, VM_UVMEXP2};
448 long pagesize = getpagesize();
449
450 size = sizeof(uv);
451 if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) {
452 PyErr_SetFromErrno(PyExc_OSError);
453 return NULL;
454 }
455
456 return Py_BuildValue("KKKKKKKK",
457 (unsigned long long) uv.npages << uv.pageshift, // total
458 (unsigned long long) uv.free << uv.pageshift, // free
459 (unsigned long long) uv.active << uv.pageshift, // active
460 (unsigned long long) uv.inactive << uv.pageshift, // inactive
461 (unsigned long long) uv.wired << uv.pageshift, // wired
462 (unsigned long long) uv.filepages + uv.execpages * pagesize, // cached
463 // These are determined from /proc/meminfo in Python.
464 (unsigned long long) 0, // buffers
465 (unsigned long long) 0 // shared
466 );
467 }
468
469
470 PyObject *
psutil_swap_mem(PyObject * self,PyObject * args)471 psutil_swap_mem(PyObject *self, PyObject *args) {
472 uint64_t swap_total, swap_free;
473 struct swapent *swdev;
474 int nswap, i;
475
476 nswap = swapctl(SWAP_NSWAP, 0, 0);
477 if (nswap == 0) {
478 // This means there's no swap partition.
479 return Py_BuildValue("(iiiii)", 0, 0, 0, 0, 0);
480 }
481
482 swdev = calloc(nswap, sizeof(*swdev));
483 if (swdev == NULL) {
484 PyErr_SetFromErrno(PyExc_OSError);
485 return NULL;
486 }
487
488 if (swapctl(SWAP_STATS, swdev, nswap) == -1) {
489 PyErr_SetFromErrno(PyExc_OSError);
490 goto error;
491 }
492
493 // Total things up.
494 swap_total = swap_free = 0;
495 for (i = 0; i < nswap; i++) {
496 if (swdev[i].se_flags & SWF_ENABLE) {
497 swap_total += swdev[i].se_nblks * DEV_BSIZE;
498 swap_free += (swdev[i].se_nblks - swdev[i].se_inuse) * DEV_BSIZE;
499 }
500 }
501 free(swdev);
502
503 // Get swap in/out
504 unsigned int total;
505 size_t size = sizeof(total);
506 struct uvmexp_sysctl uv;
507 int mib[] = {CTL_VM, VM_UVMEXP2};
508 long pagesize = getpagesize();
509 size = sizeof(uv);
510 if (sysctl(mib, 2, &uv, &size, NULL, 0) < 0) {
511 PyErr_SetFromErrno(PyExc_OSError);
512 goto error;
513 }
514
515 return Py_BuildValue("(LLLll)",
516 swap_total,
517 (swap_total - swap_free),
518 swap_free,
519 (long) uv.pgswapin * pagesize, // swap in
520 (long) uv.pgswapout * pagesize); // swap out
521
522 error:
523 free(swdev);
524 return NULL;
525 }
526
527
528 PyObject *
psutil_proc_num_fds(PyObject * self,PyObject * args)529 psutil_proc_num_fds(PyObject *self, PyObject *args) {
530 long pid;
531 int cnt;
532
533 struct kinfo_file *freep;
534
535 if (! PyArg_ParseTuple(args, "l", &pid))
536 return NULL;
537
538 errno = 0;
539 freep = kinfo_getfile(pid, &cnt);
540 if (freep == NULL) {
541 psutil_raise_for_pid(pid, "kinfo_getfile()");
542 return NULL;
543 }
544 free(freep);
545
546 return Py_BuildValue("i", cnt);
547 }
548
549
550 PyObject *
psutil_per_cpu_times(PyObject * self,PyObject * args)551 psutil_per_cpu_times(PyObject *self, PyObject *args) {
552 // XXX: why static?
553 int mib[3];
554 int ncpu;
555 size_t len;
556 size_t size;
557 int i;
558 PyObject *py_cputime = NULL;
559 PyObject *py_retlist = PyList_New(0);
560
561 if (py_retlist == NULL)
562 return NULL;
563 // retrieve the number of cpus
564 mib[0] = CTL_HW;
565 mib[1] = HW_NCPU;
566 len = sizeof(ncpu);
567 if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
568 PyErr_SetFromErrno(PyExc_OSError);
569 goto error;
570 }
571 uint64_t cpu_time[CPUSTATES];
572
573 for (i = 0; i < ncpu; i++) {
574 // per-cpu info
575 mib[0] = CTL_KERN;
576 mib[1] = KERN_CP_TIME;
577 mib[2] = i;
578 size = sizeof(cpu_time);
579 if (sysctl(mib, 3, &cpu_time, &size, NULL, 0) == -1) {
580 warn("failed to get kern.cptime2");
581 PyErr_SetFromErrno(PyExc_OSError);
582 return NULL;
583 }
584
585 py_cputime = Py_BuildValue(
586 "(ddddd)",
587 (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,
588 (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,
589 (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,
590 (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,
591 (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC);
592 if (!py_cputime)
593 goto error;
594 if (PyList_Append(py_retlist, py_cputime))
595 goto error;
596 Py_DECREF(py_cputime);
597 }
598
599 return py_retlist;
600
601 error:
602 Py_XDECREF(py_cputime);
603 Py_DECREF(py_retlist);
604 return NULL;
605 }
606
607
608 PyObject *
psutil_disk_io_counters(PyObject * self,PyObject * args)609 psutil_disk_io_counters(PyObject *self, PyObject *args) {
610 int i, dk_ndrive, mib[3];
611 size_t len;
612 struct io_sysctl *stats = NULL;
613 PyObject *py_disk_info = NULL;
614 PyObject *py_retdict = PyDict_New();
615
616 if (py_retdict == NULL)
617 return NULL;
618 mib[0] = CTL_HW;
619 mib[1] = HW_IOSTATS;
620 mib[2] = sizeof(struct io_sysctl);
621 len = 0;
622 if (sysctl(mib, 3, NULL, &len, NULL, 0) < 0) {
623 warn("can't get HW_IOSTATS");
624 PyErr_SetFromErrno(PyExc_OSError);
625 goto error;
626 }
627 dk_ndrive = (int)(len / sizeof(struct io_sysctl));
628
629 stats = malloc(len);
630 if (stats == NULL) {
631 PyErr_NoMemory();
632 goto error;
633 }
634 if (sysctl(mib, 3, stats, &len, NULL, 0) < 0 ) {
635 PyErr_SetFromErrno(PyExc_OSError);
636 goto error;
637 }
638
639 for (i = 0; i < dk_ndrive; i++) {
640 py_disk_info = Py_BuildValue(
641 "(KKKK)",
642 stats[i].rxfer,
643 stats[i].wxfer,
644 stats[i].rbytes,
645 stats[i].wbytes
646 );
647 if (!py_disk_info)
648 goto error;
649 if (PyDict_SetItemString(py_retdict, stats[i].name, py_disk_info))
650 goto error;
651 Py_DECREF(py_disk_info);
652 }
653
654 free(stats);
655 return py_retdict;
656
657 error:
658 Py_XDECREF(py_disk_info);
659 Py_DECREF(py_retdict);
660 if (stats != NULL)
661 free(stats);
662 return NULL;
663 }
664
665
666 PyObject *
psutil_cpu_stats(PyObject * self,PyObject * args)667 psutil_cpu_stats(PyObject *self, PyObject *args) {
668 size_t size;
669 struct uvmexp_sysctl uv;
670 int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2};
671
672 size = sizeof(uv);
673 if (sysctl(uvmexp_mib, 2, &uv, &size, NULL, 0) < 0) {
674 PyErr_SetFromErrno(PyExc_OSError);
675 return NULL;
676 }
677
678 return Py_BuildValue(
679 "IIIIIII",
680 uv.swtch, // ctx switches
681 uv.intrs, // interrupts - XXX always 0, will be determined via /proc
682 uv.softs, // soft interrupts
683 uv.syscalls, // syscalls - XXX always 0
684 uv.traps, // traps
685 uv.faults, // faults
686 uv.forks // forks
687 );
688 }
689