1 /*
2  * Copyright (c) 2009, Giampaolo Rodola', Landry Breuil (OpenBSD).
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 FreeBSD and OpenBSD.
8 
9  * OpenBSD references:
10  * - OpenBSD source code: https://github.com/openbsd/src
11  *
12  * OpenBSD / NetBSD: missing APIs compared to FreeBSD implementation:
13  * - psutil.net_connections()
14  * - psutil.Process.get/set_cpu_affinity()  (not supported natively)
15  * - psutil.Process.memory_maps()
16  */
17 
18 #if defined(PSUTIL_NETBSD)
19     #define _KMEMUSER
20 #endif
21 
22 #include <Python.h>
23 #include <assert.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <signal.h>
28 #include <fcntl.h>
29 #include <paths.h>
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/sysctl.h>
33 #if !defined(__NetBSD__)
34     #include <sys/user.h>
35 #endif
36 #include <sys/proc.h>
37 #include <sys/file.h>
38 #include <sys/socket.h>
39 #include <net/route.h>
40 #include <sys/socketvar.h>    // for struct xsocket
41 #include <sys/un.h>
42 #include <sys/unpcb.h>
43 // for xinpcb struct
44 #include <netinet/in.h>
45 #include <netinet/in_systm.h>
46 #include <netinet/ip.h>
47 #include <netinet/in_pcb.h>
48 #include <netinet/tcp.h>
49 #include <netinet/tcp_timer.h>
50 #include <netinet/ip_var.h>
51 #include <netinet/tcp_var.h>   // for struct xtcpcb
52 #include <netinet/tcp_fsm.h>   // for TCP connection states
53 #include <arpa/inet.h>         // for inet_ntop()
54 #include <sys/mount.h>
55 #include <net/if.h>       // net io counters
56 #include <net/if_dl.h>
57 #include <net/route.h>
58 #include <netinet/in.h>   // process open files/connections
59 #include <sys/un.h>
60 
61 #include "_psutil_common.h"
62 #include "_psutil_posix.h"
63 
64 #ifdef PSUTIL_FREEBSD
65     #include "arch/freebsd/specific.h"
66     #include "arch/freebsd/sys_socks.h"
67     #include "arch/freebsd/proc_socks.h"
68 
69     #include <net/if_media.h>
70     #include <devstat.h>  // get io counters
71     #include <libutil.h>  // process open files, shared libs (kinfo_getvmmap)
72     #if __FreeBSD_version < 900000
73         #include <utmp.h>  // system users
74     #else
75         #include <utmpx.h>
76     #endif
77 #elif PSUTIL_OPENBSD
78     #include "arch/openbsd/specific.h"
79 
80     #include <utmp.h>
81     #include <sys/vnode.h>  // for VREG
82     #define _KERNEL  // for DTYPE_VNODE
83     #include <sys/file.h>
84     #undef _KERNEL
85     #include <sys/sched.h>  // for CPUSTATES & CP_*
86 #elif PSUTIL_NETBSD
87     #include "arch/netbsd/specific.h"
88     #include "arch/netbsd/socks.h"
89 
90     #include <utmpx.h>
91     #include <sys/vnode.h>  // for VREG
92     #include <sys/sched.h>  // for CPUSTATES & CP_*
93     #ifndef DTYPE_VNODE
94         #define DTYPE_VNODE 1
95     #endif
96 #endif
97 
98 
99 // convert a timeval struct to a double
100 #define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
101 
102 #ifdef PSUTIL_FREEBSD
103     // convert a bintime struct to milliseconds
104     #define PSUTIL_BT2MSEC(bt) (bt.sec * 1000 + (((uint64_t) 1000000000 * \
105                            (uint32_t) (bt.frac >> 32) ) >> 32 ) / 1000000)
106 #endif
107 
108 #if defined(PSUTIL_OPENBSD) || defined (PSUTIL_NETBSD)
109     #define PSUTIL_KPT2DOUBLE(t) (t ## _sec + t ## _usec / 1000000.0)
110 #endif
111 
112 
113 /*
114  * Return a Python list of all the PIDs running on the system.
115  */
116 static PyObject *
psutil_pids(PyObject * self,PyObject * args)117 psutil_pids(PyObject *self, PyObject *args) {
118     kinfo_proc *proclist = NULL;
119     kinfo_proc *orig_address = NULL;
120     size_t num_processes;
121     size_t idx;
122     PyObject *py_retlist = PyList_New(0);
123     PyObject *py_pid = NULL;
124 
125     if (py_retlist == NULL)
126         return NULL;
127 
128     if (psutil_get_proc_list(&proclist, &num_processes) != 0)
129         goto error;
130 
131     if (num_processes > 0) {
132         orig_address = proclist; // save so we can free it after we're done
133         for (idx = 0; idx < num_processes; idx++) {
134 #ifdef PSUTIL_FREEBSD
135             py_pid = PyLong_FromPid(proclist->ki_pid);
136 #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
137             py_pid = PyLong_FromPid(proclist->p_pid);
138 #endif
139             if (!py_pid)
140                 goto error;
141             if (PyList_Append(py_retlist, py_pid))
142                 goto error;
143             Py_CLEAR(py_pid);
144             proclist++;
145         }
146         free(orig_address);
147     }
148 
149     return py_retlist;
150 
151 error:
152     Py_XDECREF(py_pid);
153     Py_DECREF(py_retlist);
154     if (orig_address != NULL)
155         free(orig_address);
156     return NULL;
157 }
158 
159 
160 /*
161  * Return a Python float indicating the system boot time expressed in
162  * seconds since the epoch.
163  */
164 static PyObject *
psutil_boot_time(PyObject * self,PyObject * args)165 psutil_boot_time(PyObject *self, PyObject *args) {
166     // fetch sysctl "kern.boottime"
167     static int request[2] = { CTL_KERN, KERN_BOOTTIME };
168     struct timeval boottime;
169     size_t len = sizeof(boottime);
170 
171     if (sysctl(request, 2, &boottime, &len, NULL, 0) == -1)
172         return PyErr_SetFromErrno(PyExc_OSError);
173     return Py_BuildValue("d", (double)boottime.tv_sec);
174 }
175 
176 
177 /*
178  * Collect different info about a process in one shot and return
179  * them as a big Python tuple.
180  */
181 static PyObject *
psutil_proc_oneshot_info(PyObject * self,PyObject * args)182 psutil_proc_oneshot_info(PyObject *self, PyObject *args) {
183     pid_t pid;
184     long rss;
185     long vms;
186     long memtext;
187     long memdata;
188     long memstack;
189     int oncpu;
190     kinfo_proc kp;
191     long pagesize = sysconf(_SC_PAGESIZE);
192     char str[1000];
193     PyObject *py_name;
194     PyObject *py_ppid;
195     PyObject *py_retlist;
196 
197     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
198         return NULL;
199     if (psutil_kinfo_proc(pid, &kp) == -1)
200         return NULL;
201 
202     // Process
203 #ifdef PSUTIL_FREEBSD
204     sprintf(str, "%s", kp.ki_comm);
205 #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
206     sprintf(str, "%s", kp.p_comm);
207 #endif
208     py_name = PyUnicode_DecodeFSDefault(str);
209     if (! py_name) {
210         // Likely a decoding error. We don't want to fail the whole
211         // operation. The python module may retry with proc_name().
212         PyErr_Clear();
213         py_name = Py_None;
214     }
215     // Py_INCREF(py_name);
216 
217     // Calculate memory.
218 #ifdef PSUTIL_FREEBSD
219     rss = (long)kp.ki_rssize * pagesize;
220     vms = (long)kp.ki_size;
221     memtext = (long)kp.ki_tsize * pagesize;
222     memdata = (long)kp.ki_dsize * pagesize;
223     memstack = (long)kp.ki_ssize * pagesize;
224 #else
225     rss = (long)kp.p_vm_rssize * pagesize;
226     #ifdef PSUTIL_OPENBSD
227         // VMS, this is how ps determines it on OpenBSD:
228         // https://github.com/openbsd/src/blob/
229         //     588f7f8c69786211f2d16865c552afb91b1c7cba/bin/ps/print.c#L505
230         vms = (long)(kp.p_vm_dsize + kp.p_vm_ssize + kp.p_vm_tsize) * pagesize;
231     #elif PSUTIL_NETBSD
232         // VMS, this is how top determines it on NetBSD:
233         // https://github.com/IIJ-NetBSD/netbsd-src/blob/master/external/
234         //     bsd/top/dist/machine/m_netbsd.c
235         vms = (long)kp.p_vm_msize * pagesize;
236     #endif
237         memtext = (long)kp.p_vm_tsize * pagesize;
238         memdata = (long)kp.p_vm_dsize * pagesize;
239         memstack = (long)kp.p_vm_ssize * pagesize;
240 #endif
241 
242 #ifdef PSUTIL_FREEBSD
243     // what CPU we're on; top was used as an example:
244     // https://svnweb.freebsd.org/base/head/usr.bin/top/machine.c?
245     //     view=markup&pathrev=273835
246     // XXX - note: for "intr" PID this is -1.
247     if (kp.ki_stat == SRUN && kp.ki_oncpu != NOCPU)
248         oncpu = kp.ki_oncpu;
249     else
250         oncpu = kp.ki_lastcpu;
251 #else
252     // On Net/OpenBSD we have kp.p_cpuid but it appears it's always
253     // set to KI_NOCPU. Even if it's not, ki_lastcpu does not exist
254     // so there's no way to determine where "sleeping" processes
255     // were. Not supported.
256     oncpu = -1;
257 #endif
258 
259 #ifdef PSUTIL_FREEBSD
260     py_ppid = PyLong_FromPid(kp.ki_ppid);
261 #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
262     py_ppid = PyLong_FromPid(kp.p_ppid);
263 #else
264     py_ppid = Py_BuildfValue(-1);
265 #endif
266     if (! py_ppid)
267         return NULL;
268 
269     // Return a single big tuple with all process info.
270     py_retlist = Py_BuildValue(
271 #if defined(__FreeBSD_version) && __FreeBSD_version >= 1200031
272         "(OillllllLdllllddddlllllbO)",
273 #else
274         "(OillllllidllllddddlllllbO)",
275 #endif
276 #ifdef PSUTIL_FREEBSD
277         py_ppid,                         // (pid_t) ppid
278         (int)kp.ki_stat,                 // (int) status
279         // UIDs
280         (long)kp.ki_ruid,                // (long) real uid
281         (long)kp.ki_uid,                 // (long) effective uid
282         (long)kp.ki_svuid,               // (long) saved uid
283         // GIDs
284         (long)kp.ki_rgid,                // (long) real gid
285         (long)kp.ki_groups[0],           // (long) effective gid
286         (long)kp.ki_svuid,               // (long) saved gid
287         //
288         kp.ki_tdev,                      // (int or long long) tty nr
289         PSUTIL_TV2DOUBLE(kp.ki_start),   // (double) create time
290         // ctx switches
291         kp.ki_rusage.ru_nvcsw,           // (long) ctx switches (voluntary)
292         kp.ki_rusage.ru_nivcsw,          // (long) ctx switches (unvoluntary)
293         // IO count
294         kp.ki_rusage.ru_inblock,         // (long) read io count
295         kp.ki_rusage.ru_oublock,         // (long) write io count
296         // CPU times: convert from micro seconds to seconds.
297         PSUTIL_TV2DOUBLE(kp.ki_rusage.ru_utime),     // (double) user time
298         PSUTIL_TV2DOUBLE(kp.ki_rusage.ru_stime),     // (double) sys time
299         PSUTIL_TV2DOUBLE(kp.ki_rusage_ch.ru_utime),  // (double) children utime
300         PSUTIL_TV2DOUBLE(kp.ki_rusage_ch.ru_stime),  // (double) children stime
301         // memory
302         rss,                              // (long) rss
303         vms,                              // (long) vms
304         memtext,                          // (long) mem text
305         memdata,                          // (long) mem data
306         memstack,                         // (long) mem stack
307         // others
308         oncpu,                            // (int) the CPU we are on
309 #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
310         py_ppid,                         // (pid_t) ppid
311         (int)kp.p_stat,                  // (int) status
312         // UIDs
313         (long)kp.p_ruid,                 // (long) real uid
314         (long)kp.p_uid,                  // (long) effective uid
315         (long)kp.p_svuid,                // (long) saved uid
316         // GIDs
317         (long)kp.p_rgid,                 // (long) real gid
318         (long)kp.p_groups[0],            // (long) effective gid
319         (long)kp.p_svuid,                // (long) saved gid
320         //
321         kp.p_tdev,                       // (int) tty nr
322         PSUTIL_KPT2DOUBLE(kp.p_ustart),  // (double) create time
323         // ctx switches
324         kp.p_uru_nvcsw,                  // (long) ctx switches (voluntary)
325         kp.p_uru_nivcsw,                 // (long) ctx switches (unvoluntary)
326         // IO count
327         kp.p_uru_inblock,                // (long) read io count
328         kp.p_uru_oublock,                // (long) write io count
329         // CPU times: convert from micro seconds to seconds.
330         PSUTIL_KPT2DOUBLE(kp.p_uutime),  // (double) user time
331         PSUTIL_KPT2DOUBLE(kp.p_ustime),  // (double) sys time
332         // OpenBSD and NetBSD provide children user + system times summed
333         // together (no distinction).
334         kp.p_uctime_sec + kp.p_uctime_usec / 1000000.0,  // (double) ch utime
335         kp.p_uctime_sec + kp.p_uctime_usec / 1000000.0,  // (double) ch stime
336         // memory
337         rss,                              // (long) rss
338         vms,                              // (long) vms
339         memtext,                          // (long) mem text
340         memdata,                          // (long) mem data
341         memstack,                         // (long) mem stack
342         // others
343         oncpu,                            // (int) the CPU we are on
344 #endif
345         py_name                           // (pystr) name
346     );
347 
348     Py_DECREF(py_name);
349     Py_DECREF(py_ppid);
350     return py_retlist;
351 }
352 
353 
354 /*
355  * Return process name from kinfo_proc as a Python string.
356  */
357 static PyObject *
psutil_proc_name(PyObject * self,PyObject * args)358 psutil_proc_name(PyObject *self, PyObject *args) {
359     pid_t pid;
360     kinfo_proc kp;
361     char str[1000];
362 
363     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
364         return NULL;
365     if (psutil_kinfo_proc(pid, &kp) == -1)
366         return NULL;
367 
368 #ifdef PSUTIL_FREEBSD
369     sprintf(str, "%s", kp.ki_comm);
370 #elif defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD)
371     sprintf(str, "%s", kp.p_comm);
372 #endif
373     return PyUnicode_DecodeFSDefault(str);
374 }
375 
376 
377 /*
378  * Return process cmdline as a Python list of cmdline arguments.
379  */
380 static PyObject *
psutil_proc_cmdline(PyObject * self,PyObject * args)381 psutil_proc_cmdline(PyObject *self, PyObject *args) {
382     pid_t pid;
383     PyObject *py_retlist = NULL;
384 
385     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
386         return NULL;
387     py_retlist = psutil_get_cmdline(pid);
388     if (py_retlist == NULL)
389         return NULL;
390     return Py_BuildValue("N", py_retlist);
391 }
392 
393 
394 /*
395  * Return the number of logical CPUs in the system.
396  * XXX this could be shared with macOS
397  */
398 static PyObject *
psutil_cpu_count_logical(PyObject * self,PyObject * args)399 psutil_cpu_count_logical(PyObject *self, PyObject *args) {
400     int mib[2];
401     int ncpu;
402     size_t len;
403 
404     mib[0] = CTL_HW;
405     mib[1] = HW_NCPU;
406     len = sizeof(ncpu);
407 
408     if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1)
409         Py_RETURN_NONE;  // mimic os.cpu_count()
410     else
411         return Py_BuildValue("i", ncpu);
412 }
413 
414 
415 /*
416  * Return a Python tuple representing user, kernel and idle CPU times
417  */
418 static PyObject *
psutil_cpu_times(PyObject * self,PyObject * args)419 psutil_cpu_times(PyObject *self, PyObject *args) {
420 #ifdef PSUTIL_NETBSD
421     u_int64_t cpu_time[CPUSTATES];
422 #else
423     long cpu_time[CPUSTATES];
424 #endif
425     size_t size = sizeof(cpu_time);
426     int ret;
427 
428 #if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD)
429     ret = sysctlbyname("kern.cp_time", &cpu_time, &size, NULL, 0);
430 #elif PSUTIL_OPENBSD
431     int mib[] = {CTL_KERN, KERN_CPTIME};
432     ret = sysctl(mib, 2, &cpu_time, &size, NULL, 0);
433 #endif
434     if (ret == -1)
435         return PyErr_SetFromErrno(PyExc_OSError);
436     return Py_BuildValue("(ddddd)",
437                          (double)cpu_time[CP_USER] / CLOCKS_PER_SEC,
438                          (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC,
439                          (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC,
440                          (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC,
441                          (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC
442                         );
443 }
444 
445 
446  /*
447  * Return files opened by process as a list of (path, fd) tuples.
448  * TODO: this is broken as it may report empty paths. 'procstat'
449  * utility has the same problem see:
450  * https://github.com/giampaolo/psutil/issues/595
451  */
452 #if (defined(__FreeBSD_version) && __FreeBSD_version >= 800000) || PSUTIL_OPENBSD || defined(PSUTIL_NETBSD)
453 static PyObject *
psutil_proc_open_files(PyObject * self,PyObject * args)454 psutil_proc_open_files(PyObject *self, PyObject *args) {
455     pid_t pid;
456     int i;
457     int cnt;
458     int regular;
459     int fd;
460     char *path;
461     struct kinfo_file *freep = NULL;
462     struct kinfo_file *kif;
463     kinfo_proc kipp;
464     PyObject *py_tuple = NULL;
465     PyObject *py_path = NULL;
466     PyObject *py_retlist = PyList_New(0);
467 
468     if (py_retlist == NULL)
469         return NULL;
470     if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
471         goto error;
472     if (psutil_kinfo_proc(pid, &kipp) == -1)
473         goto error;
474 
475     errno = 0;
476     freep = kinfo_getfile(pid, &cnt);
477     if (freep == NULL) {
478 #if !defined(PSUTIL_OPENBSD)
479         psutil_raise_for_pid(pid, "kinfo_getfile()");
480 #endif
481         goto error;
482     }
483 
484     for (i = 0; i < cnt; i++) {
485         kif = &freep[i];
486 
487 #ifdef PSUTIL_FREEBSD
488         regular = (kif->kf_type == KF_TYPE_VNODE) && \
489             (kif->kf_vnode_type == KF_VTYPE_VREG);
490         fd = kif->kf_fd;
491         path = kif->kf_path;
492 #elif PSUTIL_OPENBSD
493         regular = (kif->f_type == DTYPE_VNODE) && (kif->v_type == VREG);
494         fd = kif->fd_fd;
495         // XXX - it appears path is not exposed in the kinfo_file struct.
496         path = "";
497 #elif PSUTIL_NETBSD
498         regular = (kif->ki_ftype == DTYPE_VNODE) && (kif->ki_vtype == VREG);
499         fd = kif->ki_fd;
500         // XXX - it appears path is not exposed in the kinfo_file struct.
501         path = "";
502 #endif
503         if (regular == 1) {
504             py_path = PyUnicode_DecodeFSDefault(path);
505             if (! py_path)
506                 goto error;
507             py_tuple = Py_BuildValue("(Oi)", py_path, fd);
508             if (py_tuple == NULL)
509                 goto error;
510             if (PyList_Append(py_retlist, py_tuple))
511                 goto error;
512             Py_CLEAR(py_path);
513             Py_CLEAR(py_tuple);
514         }
515     }
516     free(freep);
517     return py_retlist;
518 
519 error:
520     Py_XDECREF(py_tuple);
521     Py_DECREF(py_retlist);
522     if (freep != NULL)
523         free(freep);
524     return NULL;
525 }
526 #endif
527 
528 
529 /*
530  * Return a list of tuples including device, mount point and fs type
531  * for all partitions mounted on the system.
532  */
533 static PyObject *
psutil_disk_partitions(PyObject * self,PyObject * args)534 psutil_disk_partitions(PyObject *self, PyObject *args) {
535     int num;
536     int i;
537     long len;
538     uint64_t flags;
539     char opts[200];
540 #ifdef PSUTIL_NETBSD
541     struct statvfs *fs = NULL;
542 #else
543     struct statfs *fs = NULL;
544 #endif
545     PyObject *py_retlist = PyList_New(0);
546     PyObject *py_dev = NULL;
547     PyObject *py_mountp = NULL;
548     PyObject *py_tuple = NULL;
549 
550     if (py_retlist == NULL)
551         return NULL;
552 
553     // get the number of mount points
554     Py_BEGIN_ALLOW_THREADS
555 #ifdef PSUTIL_NETBSD
556     num = getvfsstat(NULL, 0, MNT_NOWAIT);
557 #else
558     num = getfsstat(NULL, 0, MNT_NOWAIT);
559 #endif
560     Py_END_ALLOW_THREADS
561     if (num == -1) {
562         PyErr_SetFromErrno(PyExc_OSError);
563         goto error;
564     }
565 
566     len = sizeof(*fs) * num;
567     fs = malloc(len);
568     if (fs == NULL) {
569         PyErr_NoMemory();
570         goto error;
571     }
572 
573     Py_BEGIN_ALLOW_THREADS
574 #ifdef PSUTIL_NETBSD
575     num = getvfsstat(fs, len, MNT_NOWAIT);
576 #else
577     num = getfsstat(fs, len, MNT_NOWAIT);
578 #endif
579     Py_END_ALLOW_THREADS
580     if (num == -1) {
581         PyErr_SetFromErrno(PyExc_OSError);
582         goto error;
583     }
584 
585     for (i = 0; i < num; i++) {
586         py_tuple = NULL;
587         opts[0] = 0;
588 #ifdef PSUTIL_NETBSD
589         flags = fs[i].f_flag;
590 #else
591         flags = fs[i].f_flags;
592 #endif
593 
594         // see sys/mount.h
595         if (flags & MNT_RDONLY)
596             strlcat(opts, "ro", sizeof(opts));
597         else
598             strlcat(opts, "rw", sizeof(opts));
599         if (flags & MNT_SYNCHRONOUS)
600             strlcat(opts, ",sync", sizeof(opts));
601         if (flags & MNT_NOEXEC)
602             strlcat(opts, ",noexec", sizeof(opts));
603         if (flags & MNT_NOSUID)
604             strlcat(opts, ",nosuid", sizeof(opts));
605         if (flags & MNT_ASYNC)
606             strlcat(opts, ",async", sizeof(opts));
607         if (flags & MNT_NOATIME)
608             strlcat(opts, ",noatime", sizeof(opts));
609         if (flags & MNT_SOFTDEP)
610             strlcat(opts, ",softdep", sizeof(opts));
611 #ifdef PSUTIL_FREEBSD
612         if (flags & MNT_UNION)
613             strlcat(opts, ",union", sizeof(opts));
614         if (flags & MNT_SUIDDIR)
615             strlcat(opts, ",suiddir", sizeof(opts));
616         if (flags & MNT_SOFTDEP)
617             strlcat(opts, ",softdep", sizeof(opts));
618         if (flags & MNT_NOSYMFOLLOW)
619             strlcat(opts, ",nosymfollow", sizeof(opts));
620         if (flags & MNT_GJOURNAL)
621             strlcat(opts, ",gjournal", sizeof(opts));
622         if (flags & MNT_MULTILABEL)
623             strlcat(opts, ",multilabel", sizeof(opts));
624         if (flags & MNT_ACLS)
625             strlcat(opts, ",acls", sizeof(opts));
626         if (flags & MNT_NOCLUSTERR)
627             strlcat(opts, ",noclusterr", sizeof(opts));
628         if (flags & MNT_NOCLUSTERW)
629             strlcat(opts, ",noclusterw", sizeof(opts));
630         if (flags & MNT_NFS4ACLS)
631             strlcat(opts, ",nfs4acls", sizeof(opts));
632 #elif PSUTIL_NETBSD
633         if (flags & MNT_NODEV)
634             strlcat(opts, ",nodev", sizeof(opts));
635         if (flags & MNT_UNION)
636             strlcat(opts, ",union", sizeof(opts));
637         if (flags & MNT_NOCOREDUMP)
638             strlcat(opts, ",nocoredump", sizeof(opts));
639 #ifdef MNT_RELATIME
640         if (flags & MNT_RELATIME)
641             strlcat(opts, ",relatime", sizeof(opts));
642 #endif
643         if (flags & MNT_IGNORE)
644             strlcat(opts, ",ignore", sizeof(opts));
645 #ifdef MNT_DISCARD
646         if (flags & MNT_DISCARD)
647             strlcat(opts, ",discard", sizeof(opts));
648 #endif
649 #ifdef MNT_EXTATTR
650         if (flags & MNT_EXTATTR)
651             strlcat(opts, ",extattr", sizeof(opts));
652 #endif
653         if (flags & MNT_LOG)
654             strlcat(opts, ",log", sizeof(opts));
655         if (flags & MNT_SYMPERM)
656             strlcat(opts, ",symperm", sizeof(opts));
657         if (flags & MNT_NODEVMTIME)
658             strlcat(opts, ",nodevmtime", sizeof(opts));
659 #endif
660         py_dev = PyUnicode_DecodeFSDefault(fs[i].f_mntfromname);
661         if (! py_dev)
662             goto error;
663         py_mountp = PyUnicode_DecodeFSDefault(fs[i].f_mntonname);
664         if (! py_mountp)
665             goto error;
666         py_tuple = Py_BuildValue("(OOss)",
667                                  py_dev,               // device
668                                  py_mountp,            // mount point
669                                  fs[i].f_fstypename,   // fs type
670                                  opts);                // options
671         if (!py_tuple)
672             goto error;
673         if (PyList_Append(py_retlist, py_tuple))
674             goto error;
675         Py_CLEAR(py_dev);
676         Py_CLEAR(py_mountp);
677         Py_CLEAR(py_tuple);
678     }
679 
680     free(fs);
681     return py_retlist;
682 
683 error:
684     Py_XDECREF(py_dev);
685     Py_XDECREF(py_mountp);
686     Py_XDECREF(py_tuple);
687     Py_DECREF(py_retlist);
688     if (fs != NULL)
689         free(fs);
690     return NULL;
691 }
692 
693 
694 /*
695  * Return a Python list of named tuples with overall network I/O information
696  */
697 static PyObject *
psutil_net_io_counters(PyObject * self,PyObject * args)698 psutil_net_io_counters(PyObject *self, PyObject *args) {
699     char *buf = NULL, *lim, *next;
700     struct if_msghdr *ifm;
701     int mib[6];
702     size_t len;
703     PyObject *py_retdict = PyDict_New();
704     PyObject *py_ifc_info = NULL;
705     if (py_retdict == NULL)
706         return NULL;
707 
708     mib[0] = CTL_NET;          // networking subsystem
709     mib[1] = PF_ROUTE;         // type of information
710     mib[2] = 0;                // protocol (IPPROTO_xxx)
711     mib[3] = 0;                // address family
712     mib[4] = NET_RT_IFLIST;   // operation
713     mib[5] = 0;
714 
715     if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
716         PyErr_SetFromErrno(PyExc_OSError);
717         goto error;
718     }
719 
720     buf = malloc(len);
721     if (buf == NULL) {
722         PyErr_NoMemory();
723         goto error;
724     }
725 
726     if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
727         PyErr_SetFromErrno(PyExc_OSError);
728         goto error;
729     }
730 
731     lim = buf + len;
732 
733     for (next = buf; next < lim; ) {
734         py_ifc_info = NULL;
735         ifm = (struct if_msghdr *)next;
736         next += ifm->ifm_msglen;
737 
738         if (ifm->ifm_type == RTM_IFINFO) {
739             struct if_msghdr *if2m = (struct if_msghdr *)ifm;
740             struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);
741             char ifc_name[32];
742 
743             strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen);
744             ifc_name[sdl->sdl_nlen] = 0;
745             // XXX: ignore usbus interfaces:
746             // http://lists.freebsd.org/pipermail/freebsd-current/
747             //     2011-October/028752.html
748             // 'ifconfig -a' doesn't show them, nor do we.
749             if (strncmp(ifc_name, "usbus", 5) == 0)
750                 continue;
751 
752             py_ifc_info = Py_BuildValue("(kkkkkkki)",
753                                         if2m->ifm_data.ifi_obytes,
754                                         if2m->ifm_data.ifi_ibytes,
755                                         if2m->ifm_data.ifi_opackets,
756                                         if2m->ifm_data.ifi_ipackets,
757                                         if2m->ifm_data.ifi_ierrors,
758                                         if2m->ifm_data.ifi_oerrors,
759                                         if2m->ifm_data.ifi_iqdrops,
760 #ifdef _IFI_OQDROPS
761                                         if2m->ifm_data.ifi_oqdrops
762 #else
763                                         0
764 #endif
765                                         );
766             if (!py_ifc_info)
767                 goto error;
768             if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info))
769                 goto error;
770             Py_CLEAR(py_ifc_info);
771         }
772         else {
773             continue;
774         }
775     }
776 
777     free(buf);
778     return py_retdict;
779 
780 error:
781     Py_XDECREF(py_ifc_info);
782     Py_DECREF(py_retdict);
783     if (buf != NULL)
784         free(buf);
785     return NULL;
786 }
787 
788 
789 /*
790  * Return currently connected users as a list of tuples.
791  */
792 static PyObject *
psutil_users(PyObject * self,PyObject * args)793 psutil_users(PyObject *self, PyObject *args) {
794     PyObject *py_retlist = PyList_New(0);
795     PyObject *py_username = NULL;
796     PyObject *py_tty = NULL;
797     PyObject *py_hostname = NULL;
798     PyObject *py_tuple = NULL;
799     PyObject *py_pid = NULL;
800 
801     if (py_retlist == NULL)
802         return NULL;
803 
804 #if (defined(__FreeBSD_version) && (__FreeBSD_version < 900000)) || PSUTIL_OPENBSD
805     struct utmp ut;
806     FILE *fp;
807 
808     Py_BEGIN_ALLOW_THREADS
809     fp = fopen(_PATH_UTMP, "r");
810     Py_END_ALLOW_THREADS
811     if (fp == NULL) {
812         PyErr_SetFromErrnoWithFilename(PyExc_OSError, _PATH_UTMP);
813         goto error;
814     }
815 
816     while (fread(&ut, sizeof(ut), 1, fp) == 1) {
817         if (*ut.ut_name == '\0')
818             continue;
819         py_username = PyUnicode_DecodeFSDefault(ut.ut_name);
820         if (! py_username)
821             goto error;
822         py_tty = PyUnicode_DecodeFSDefault(ut.ut_line);
823         if (! py_tty)
824             goto error;
825         py_hostname = PyUnicode_DecodeFSDefault(ut.ut_host);
826         if (! py_hostname)
827             goto error;
828         py_tuple = Py_BuildValue(
829             "(OOOfi)",
830             py_username,        // username
831             py_tty,             // tty
832             py_hostname,        // hostname
833             (float)ut.ut_time,  // start time
834 #ifdef PSUTIL_OPENBSD
835             -1                  // process id (set to None later)
836 #else
837             ut.ut_pid           // TODO: use PyLong_FromPid
838 #endif
839         );
840         if (!py_tuple) {
841             fclose(fp);
842             goto error;
843         }
844         if (PyList_Append(py_retlist, py_tuple)) {
845             fclose(fp);
846             goto error;
847         }
848         Py_CLEAR(py_username);
849         Py_CLEAR(py_tty);
850         Py_CLEAR(py_hostname);
851         Py_CLEAR(py_tuple);
852     }
853 
854     fclose(fp);
855 #else
856     struct utmpx *utx;
857     setutxent();
858     while ((utx = getutxent()) != NULL) {
859         if (utx->ut_type != USER_PROCESS)
860             continue;
861         py_username = PyUnicode_DecodeFSDefault(utx->ut_user);
862         if (! py_username)
863             goto error;
864         py_tty = PyUnicode_DecodeFSDefault(utx->ut_line);
865         if (! py_tty)
866             goto error;
867         py_hostname = PyUnicode_DecodeFSDefault(utx->ut_host);
868         if (! py_hostname)
869             goto error;
870 #ifdef PSUTIL_OPENBSD
871         py_pid = Py_BuildValue("i", -1);  // set to None later
872 #else
873         py_pid = PyLong_FromPid(utx->ut_pid);
874 #endif
875         if (! py_pid)
876             goto error;
877 
878         py_tuple = Py_BuildValue(
879             "(OOOfO)",
880             py_username,   // username
881             py_tty,        // tty
882             py_hostname,   // hostname
883             (float)utx->ut_tv.tv_sec,  // start time
884             py_pid         // process id
885         );
886 
887         if (!py_tuple) {
888             endutxent();
889             goto error;
890         }
891         if (PyList_Append(py_retlist, py_tuple)) {
892             endutxent();
893             goto error;
894         }
895         Py_CLEAR(py_username);
896         Py_CLEAR(py_tty);
897         Py_CLEAR(py_hostname);
898         Py_CLEAR(py_tuple);
899         Py_CLEAR(py_pid);
900     }
901 
902     endutxent();
903 #endif
904     return py_retlist;
905 
906 error:
907     Py_XDECREF(py_username);
908     Py_XDECREF(py_tty);
909     Py_XDECREF(py_hostname);
910     Py_XDECREF(py_tuple);
911     Py_XDECREF(py_pid);
912     Py_DECREF(py_retlist);
913     return NULL;
914 }
915 
916 
917 /*
918  * define the psutil C module methods and initialize the module.
919  */
920 static PyMethodDef mod_methods[] = {
921     // --- per-process functions
922 
923     {"proc_oneshot_info", psutil_proc_oneshot_info, METH_VARARGS,
924      "Return multiple info about a process"},
925     {"proc_name", psutil_proc_name, METH_VARARGS,
926      "Return process name"},
927     {"proc_cmdline", psutil_proc_cmdline, METH_VARARGS,
928      "Return process cmdline as a list of cmdline arguments"},
929     {"proc_threads", psutil_proc_threads, METH_VARARGS,
930      "Return process threads"},
931 #if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD)
932     {"proc_connections", psutil_proc_connections, METH_VARARGS,
933      "Return connections opened by process"},
934 #endif
935     {"proc_cwd", psutil_proc_cwd, METH_VARARGS,
936      "Return process current working directory."},
937 #if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 || PSUTIL_OPENBSD || defined(PSUTIL_NETBSD)
938     {"proc_num_fds", psutil_proc_num_fds, METH_VARARGS,
939      "Return the number of file descriptors opened by this process"},
940     {"proc_open_files", psutil_proc_open_files, METH_VARARGS,
941      "Return files opened by process as a list of (path, fd) tuples"},
942 #endif
943 #if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD)
944     {"proc_num_threads", psutil_proc_num_threads, METH_VARARGS,
945      "Return number of threads used by process"},
946 #endif
947 #if defined(PSUTIL_FREEBSD)
948     {"proc_exe", psutil_proc_exe, METH_VARARGS,
949      "Return process pathname executable"},
950     {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
951      "Return a list of tuples for every process's memory map"},
952     {"proc_cpu_affinity_get", psutil_proc_cpu_affinity_get, METH_VARARGS,
953      "Return process CPU affinity."},
954     {"proc_cpu_affinity_set", psutil_proc_cpu_affinity_set, METH_VARARGS,
955      "Set process CPU affinity."},
956     {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS,
957      "Return an XML string to determine the number physical CPUs."},
958 #endif
959 
960     // --- system-related functions
961 
962     {"pids", psutil_pids, METH_VARARGS,
963      "Returns a list of PIDs currently running on the system"},
964     {"cpu_count_logical", psutil_cpu_count_logical, METH_VARARGS,
965      "Return number of logical CPUs on the system"},
966     {"virtual_mem", psutil_virtual_mem, METH_VARARGS,
967      "Return system virtual memory usage statistics"},
968     {"swap_mem", psutil_swap_mem, METH_VARARGS,
969      "Return swap mem stats"},
970     {"cpu_times", psutil_cpu_times, METH_VARARGS,
971      "Return system cpu times as a tuple (user, system, nice, idle, irc)"},
972     {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
973      "Return system per-cpu times as a list of tuples"},
974     {"boot_time", psutil_boot_time, METH_VARARGS,
975      "Return the system boot time expressed in seconds since the epoch."},
976     {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
977      "Return a list of tuples including device, mount point and "
978      "fs type for all partitions mounted on the system."},
979     {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
980      "Return dict of tuples of networks I/O information."},
981     {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
982      "Return a Python dict of tuples for disk I/O information"},
983     {"users", psutil_users, METH_VARARGS,
984      "Return currently connected users as a list of tuples"},
985     {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
986      "Return CPU statistics"},
987 #if defined(PSUTIL_FREEBSD) || defined(PSUTIL_NETBSD)
988     {"net_connections", psutil_net_connections, METH_VARARGS,
989      "Return system-wide open connections."},
990 #endif
991 #if defined(PSUTIL_FREEBSD)
992     {"sensors_battery", psutil_sensors_battery, METH_VARARGS,
993      "Return battery information."},
994     {"sensors_cpu_temperature", psutil_sensors_cpu_temperature, METH_VARARGS,
995      "Return temperature information for a given CPU core number."},
996     {"cpu_frequency", psutil_cpu_freq, METH_VARARGS,
997      "Return frequency of a given CPU"},
998 #endif
999 
1000     // --- others
1001     {"set_testing", psutil_set_testing, METH_NOARGS,
1002      "Set psutil in testing mode"},
1003 
1004     {NULL, NULL, 0, NULL}
1005 };
1006 
1007 #if PY_MAJOR_VERSION >= 3
1008     #define INITERR return NULL
1009 
1010     static struct PyModuleDef moduledef = {
1011         PyModuleDef_HEAD_INIT,
1012         "_psutil_bsd",
1013         NULL,
1014         -1,
1015         mod_methods,
1016         NULL,
1017         NULL,
1018         NULL,
1019         NULL
1020     };
1021 
PyInit__psutil_bsd(void)1022     PyObject *PyInit__psutil_bsd(void)
1023 #else  /* PY_MAJOR_VERSION */
1024     #define INITERR return
1025 
1026     void init_psutil_bsd(void)
1027 #endif  /* PY_MAJOR_VERSION */
1028 {
1029     PyObject *v;
1030 #if PY_MAJOR_VERSION >= 3
1031     PyObject *mod = PyModule_Create(&moduledef);
1032 #else
1033     PyObject *mod = Py_InitModule("_psutil_bsd", mod_methods);
1034 #endif
1035     if (mod == NULL)
1036         INITERR;
1037 
1038     if (PyModule_AddIntConstant(mod, "version", PSUTIL_VERSION)) INITERR;
1039     // process status constants
1040 
1041 #ifdef PSUTIL_FREEBSD
1042     if (PyModule_AddIntConstant(mod, "SIDL", SIDL)) INITERR;
1043     if (PyModule_AddIntConstant(mod, "SRUN", SRUN)) INITERR;
1044     if (PyModule_AddIntConstant(mod, "SSLEEP", SSLEEP)) INITERR;
1045     if (PyModule_AddIntConstant(mod, "SSTOP", SSTOP)) INITERR;
1046     if (PyModule_AddIntConstant(mod, "SZOMB", SZOMB)) INITERR;
1047     if (PyModule_AddIntConstant(mod, "SWAIT", SWAIT)) INITERR;
1048     if (PyModule_AddIntConstant(mod, "SLOCK", SLOCK)) INITERR;
1049 #elif  PSUTIL_OPENBSD
1050     if (PyModule_AddIntConstant(mod, "SIDL", SIDL)) INITERR;
1051     if (PyModule_AddIntConstant(mod, "SRUN", SRUN)) INITERR;
1052     if (PyModule_AddIntConstant(mod, "SSLEEP", SSLEEP)) INITERR;
1053     if (PyModule_AddIntConstant(mod, "SSTOP", SSTOP)) INITERR;
1054     if (PyModule_AddIntConstant(mod, "SZOMB", SZOMB)) INITERR; // unused
1055     if (PyModule_AddIntConstant(mod, "SDEAD", SDEAD)) INITERR;
1056     if (PyModule_AddIntConstant(mod, "SONPROC", SONPROC)) INITERR;
1057 #elif defined(PSUTIL_NETBSD)
1058     if (PyModule_AddIntConstant(mod, "SIDL", LSIDL)) INITERR;
1059     if (PyModule_AddIntConstant(mod, "SRUN", LSRUN)) INITERR;
1060     if (PyModule_AddIntConstant(mod, "SSLEEP", LSSLEEP)) INITERR;
1061     if (PyModule_AddIntConstant(mod, "SSTOP", LSSTOP)) INITERR;
1062     if (PyModule_AddIntConstant(mod, "SZOMB", LSZOMB)) INITERR;
1063     if (PyModule_AddIntConstant(mod, "SDEAD", LSDEAD)) INITERR;
1064     if (PyModule_AddIntConstant(mod, "SONPROC", LSONPROC)) INITERR;
1065     // unique to NetBSD
1066     if (PyModule_AddIntConstant(mod, "SSUSPENDED", LSSUSPENDED)) INITERR;
1067 #endif
1068 
1069     // connection status constants
1070     if (PyModule_AddIntConstant(mod, "TCPS_CLOSED", TCPS_CLOSED))
1071         INITERR;
1072     if (PyModule_AddIntConstant(mod, "TCPS_CLOSING", TCPS_CLOSING))
1073         INITERR;
1074     if (PyModule_AddIntConstant(mod, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT))
1075         INITERR;
1076     if (PyModule_AddIntConstant(mod, "TCPS_LISTEN", TCPS_LISTEN))
1077         INITERR;
1078     if (PyModule_AddIntConstant(mod, "TCPS_ESTABLISHED", TCPS_ESTABLISHED))
1079         INITERR;
1080     if (PyModule_AddIntConstant(mod, "TCPS_SYN_SENT", TCPS_SYN_SENT))
1081         INITERR;
1082     if (PyModule_AddIntConstant(mod, "TCPS_SYN_RECEIVED", TCPS_SYN_RECEIVED))
1083         INITERR;
1084     if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1))
1085         INITERR;
1086     if (PyModule_AddIntConstant(mod, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2))
1087         INITERR;
1088     if (PyModule_AddIntConstant(mod, "TCPS_LAST_ACK", TCPS_LAST_ACK))
1089         INITERR;
1090     if (PyModule_AddIntConstant(mod, "TCPS_TIME_WAIT", TCPS_TIME_WAIT))
1091         INITERR;
1092     // PSUTIL_CONN_NONE
1093     if (PyModule_AddIntConstant(mod, "PSUTIL_CONN_NONE", 128)) INITERR;
1094 
1095     psutil_setup();
1096 
1097     if (mod == NULL)
1098         INITERR;
1099 #if PY_MAJOR_VERSION >= 3
1100     return mod;
1101 #endif
1102 }
1103