1 /*
2  * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  *
6  * Functions specific to Sun OS Solaris platforms.
7  *
8  * Thanks to Justin Venus who originally wrote a consistent part of
9  * this in Cython which I later on translated in C.
10  */
11 
12 /* fix compilation issue on SunOS 5.10, see:
13  * https://github.com/giampaolo/psutil/issues/421
14  * https://github.com/giampaolo/psutil/issues/1077
15 */
16 
17 #define _STRUCTURED_PROC 1
18 
19 #include <Python.h>
20 
21 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
22 #  undef _FILE_OFFSET_BITS
23 #  undef _LARGEFILE64_SOURCE
24 #endif
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/proc.h>
29 #include <sys/swap.h>
30 #include <sys/sysinfo.h>
31 #include <sys/mntent.h>  // for MNTTAB
32 #include <sys/mnttab.h>
33 #include <sys/procfs.h>
34 #include <sys/sockio.h>
35 #include <sys/socket.h>
36 #include <fcntl.h>
37 #include <utmpx.h>
38 #include <kstat.h>
39 #include <sys/ioctl.h>
40 #include <sys/tihdr.h>
41 #include <stropts.h>
42 #include <inet/tcp.h>
43 #ifndef NEW_MIB_COMPLIANT
44 /*
45  * Solaris introduced NEW_MIB_COMPLIANT macro with Update 4.
46  * See https://github.com/giampaolo/psutil/issues/421
47  * Prior to Update 4, one has to include mib2 by hand.
48  */
49 #include <inet/mib2.h>
50 #endif
51 #include <arpa/inet.h>
52 #include <net/if.h>
53 #include <math.h> // fabs()
54 
55 #include "_psutil_common.h"
56 #include "_psutil_posix.h"
57 
58 #include "arch/solaris/environ.h"
59 
60 #define PSUTIL_TV2DOUBLE(t) (((t).tv_nsec * 0.000000001) + (t).tv_sec)
61 
62 
63 /*
64  * Read a file content and fills a C structure with it.
65  */
66 static int
psutil_file_to_struct(char * path,void * fstruct,size_t size)67 psutil_file_to_struct(char *path, void *fstruct, size_t size) {
68     int fd;
69     ssize_t nbytes;
70     fd = open(path, O_RDONLY);
71     if (fd == -1) {
72         PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
73         return 0;
74     }
75     nbytes = read(fd, fstruct, size);
76     if (nbytes == -1) {
77         close(fd);
78         PyErr_SetFromErrno(PyExc_OSError);
79         return 0;
80     }
81     if (nbytes != (ssize_t) size) {
82         close(fd);
83         PyErr_SetString(
84             PyExc_RuntimeError, "read() file structure size mismatch");
85         return 0;
86     }
87     close(fd);
88     return nbytes;
89 }
90 
91 
92 /*
93  * Return process ppid, rss, vms, ctime, nice, nthreads, status and tty
94  * as a Python tuple.
95  */
96 static PyObject *
psutil_proc_basic_info(PyObject * self,PyObject * args)97 psutil_proc_basic_info(PyObject *self, PyObject *args) {
98     int pid;
99     char path[1000];
100     psinfo_t info;
101     const char *procfs_path;
102 
103     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
104         return NULL;
105 
106     sprintf(path, "%s/%i/psinfo", procfs_path, pid);
107     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
108         return NULL;
109     return Py_BuildValue(
110         "ikkdiiikiiii",
111         info.pr_ppid,              // parent pid
112         info.pr_rssize,            // rss
113         info.pr_size,              // vms
114         PSUTIL_TV2DOUBLE(info.pr_start),  // create time
115         info.pr_lwp.pr_nice,       // nice
116         info.pr_nlwp,              // no. of threads
117         info.pr_lwp.pr_state,      // status code
118         info.pr_ttydev,            // tty nr
119         (int)info.pr_uid,          // real user id
120         (int)info.pr_euid,         // effective user id
121         (int)info.pr_gid,          // real group id
122         (int)info.pr_egid          // effective group id
123         );
124 }
125 
126 /*
127  * Join array of C strings to C string with delemiter dm.
128  * Omit empty records.
129  */
130 static int
cstrings_array_to_string(char ** joined,char ** array,size_t count,char dm)131 cstrings_array_to_string(char **joined, char ** array, size_t count, char dm) {
132     int i;
133     size_t total_length = 0;
134     size_t item_length = 0;
135     char *result = NULL;
136     char *last = NULL;
137 
138     if (!array || !joined)
139         return 0;
140 
141     for (i=0; i<count; i++) {
142         if (!array[i])
143             continue;
144 
145         item_length = strlen(array[i]) + 1;
146         total_length += item_length;
147     }
148 
149     if (!total_length) {
150         return 0;
151     }
152 
153     result = malloc(total_length);
154     if (!result) {
155         PyErr_NoMemory();
156         return -1;
157     }
158 
159     result[0] = '\0';
160     last = result;
161 
162     for (i=0; i<count; i++) {
163         if (!array[i])
164             continue;
165 
166         item_length = strlen(array[i]);
167         memcpy(last, array[i], item_length);
168         last[item_length] = dm;
169 
170         last += item_length + 1;
171     }
172 
173     result[total_length-1] = '\0';
174     *joined = result;
175 
176     return total_length;
177 }
178 
179 /*
180  * Return process name and args as a Python tuple.
181  */
182 static PyObject *
psutil_proc_name_and_args(PyObject * self,PyObject * args)183 psutil_proc_name_and_args(PyObject *self, PyObject *args) {
184     int pid;
185     char path[1000];
186     psinfo_t info;
187     size_t argc;
188     int joined;
189     char **argv;
190     char *argv_plain;
191     const char *procfs_path;
192     PyObject *py_name = NULL;
193     PyObject *py_args = NULL;
194     PyObject *py_retlist = NULL;
195 
196     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
197         return NULL;
198     sprintf(path, "%s/%i/psinfo", procfs_path, pid);
199     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
200         return NULL;
201 
202     py_name = PyUnicode_DecodeFSDefault(info.pr_fname);
203     if (!py_name)
204         goto error;
205 
206     /* SunOS truncates arguments to length PRARGSZ, very likely args are truncated.
207      * The only way to retrieve full command line is to parse process memory */
208     if (info.pr_argc && strlen(info.pr_psargs) == PRARGSZ-1) {
209         argv = psutil_read_raw_args(info, procfs_path, &argc);
210         if (argv) {
211             joined = cstrings_array_to_string(&argv_plain, argv, argc, ' ');
212             if (joined > 0) {
213                 py_args = PyUnicode_DecodeFSDefault(argv_plain);
214                 free(argv_plain);
215             } else if (joined < 0) {
216                 goto error;
217             }
218 
219             psutil_free_cstrings_array(argv, argc);
220         }
221     }
222 
223     /* If we can't read process memory or can't decode the result
224      * then return args from /proc. */
225     if (!py_args) {
226         PyErr_Clear();
227         py_args = PyUnicode_DecodeFSDefault(info.pr_psargs);
228     }
229 
230     /* Both methods has been failed. */
231     if (!py_args)
232         goto error;
233 
234     py_retlist = Py_BuildValue("OO", py_name, py_args);
235     if (!py_retlist)
236         goto error;
237 
238     Py_DECREF(py_name);
239     Py_DECREF(py_args);
240     return py_retlist;
241 
242 error:
243     Py_XDECREF(py_name);
244     Py_XDECREF(py_args);
245     Py_XDECREF(py_retlist);
246     return NULL;
247 }
248 
249 
250 /*
251  * Return process environ block.
252  */
253 static PyObject *
psutil_proc_environ(PyObject * self,PyObject * args)254 psutil_proc_environ(PyObject *self, PyObject *args) {
255     int pid;
256     char path[1000];
257     psinfo_t info;
258     const char *procfs_path;
259     char **env = NULL;
260     ssize_t env_count = -1;
261     char *dm;
262     int i = 0;
263     PyObject *py_envname = NULL;
264     PyObject *py_envval = NULL;
265     PyObject *py_retdict = PyDict_New();
266 
267     if (! py_retdict)
268         return PyErr_NoMemory();
269 
270     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
271         return NULL;
272 
273     sprintf(path, "%s/%i/psinfo", procfs_path, pid);
274     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
275         goto error;
276 
277     if (! info.pr_envp) {
278         AccessDenied("/proc/pid/psinfo struct not set");
279         goto error;
280     }
281 
282     env = psutil_read_raw_env(info, procfs_path, &env_count);
283     if (! env && env_count != 0)
284         goto error;
285 
286     for (i=0; i<env_count; i++) {
287         if (! env[i])
288             break;
289 
290         dm = strchr(env[i], '=');
291         if (! dm)
292             continue;
293 
294         *dm = '\0';
295 
296         py_envname = PyUnicode_DecodeFSDefault(env[i]);
297         if (! py_envname)
298             goto error;
299 
300         py_envval = PyUnicode_DecodeFSDefault(dm+1);
301         if (! py_envname)
302             goto error;
303 
304         if (PyDict_SetItem(py_retdict, py_envname, py_envval) < 0)
305             goto error;
306 
307         Py_CLEAR(py_envname);
308         Py_CLEAR(py_envval);
309     }
310 
311     psutil_free_cstrings_array(env, env_count);
312     return py_retdict;
313 
314  error:
315     if (env && env_count >= 0)
316         psutil_free_cstrings_array(env, env_count);
317 
318     Py_XDECREF(py_envname);
319     Py_XDECREF(py_envval);
320     Py_XDECREF(py_retdict);
321     return NULL;
322 }
323 
324 
325 /*
326  * Return process user and system CPU times as a Python tuple.
327  */
328 static PyObject *
psutil_proc_cpu_times(PyObject * self,PyObject * args)329 psutil_proc_cpu_times(PyObject *self, PyObject *args) {
330     int pid;
331     char path[1000];
332     pstatus_t info;
333     const char *procfs_path;
334 
335     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
336         return NULL;
337     sprintf(path, "%s/%i/status", procfs_path, pid);
338     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
339         return NULL;
340     // results are more precise than os.times()
341     return Py_BuildValue(
342         "(dddd)",
343          PSUTIL_TV2DOUBLE(info.pr_utime),
344          PSUTIL_TV2DOUBLE(info.pr_stime),
345          PSUTIL_TV2DOUBLE(info.pr_cutime),
346          PSUTIL_TV2DOUBLE(info.pr_cstime)
347     );
348 }
349 
350 
351 /*
352  * Return what CPU the process is running on.
353  */
354 static PyObject *
psutil_proc_cpu_num(PyObject * self,PyObject * args)355 psutil_proc_cpu_num(PyObject *self, PyObject *args) {
356     int fd = NULL;
357     int pid;
358     char path[1000];
359     struct prheader header;
360     struct lwpsinfo *lwp = NULL;
361     int nent;
362     int size;
363     int proc_num;
364     ssize_t nbytes;
365     const char *procfs_path;
366 
367     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
368         return NULL;
369 
370     sprintf(path, "%s/%i/lpsinfo", procfs_path, pid);
371     fd = open(path, O_RDONLY);
372     if (fd == -1) {
373         PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
374         return NULL;
375     }
376 
377     // read header
378     nbytes = pread(fd, &header, sizeof(header), 0);
379     if (nbytes == -1) {
380         PyErr_SetFromErrno(PyExc_OSError);
381         goto error;
382     }
383     if (nbytes != sizeof(header)) {
384         PyErr_SetString(
385             PyExc_RuntimeError, "read() file structure size mismatch");
386         goto error;
387     }
388 
389     // malloc
390     nent = header.pr_nent;
391     size = header.pr_entsize * nent;
392     lwp = malloc(size);
393     if (lwp == NULL) {
394         PyErr_NoMemory();
395         goto error;
396     }
397 
398     // read the rest
399     nbytes = pread(fd, lwp, size, sizeof(header));
400     if (nbytes == -1) {
401         PyErr_SetFromErrno(PyExc_OSError);
402         goto error;
403     }
404     if (nbytes != size) {
405         PyErr_SetString(
406             PyExc_RuntimeError, "read() file structure size mismatch");
407         goto error;
408     }
409 
410     // done
411     proc_num = lwp->pr_onpro;
412     close(fd);
413     free(lwp);
414     return Py_BuildValue("i", proc_num);
415 
416 error:
417     if (fd != -1)
418         close(fd);
419     free(lwp);
420     return NULL;
421 }
422 
423 
424 /*
425  * Return process uids/gids as a Python tuple.
426  */
427 static PyObject *
psutil_proc_cred(PyObject * self,PyObject * args)428 psutil_proc_cred(PyObject *self, PyObject *args) {
429     int pid;
430     char path[1000];
431     prcred_t info;
432     const char *procfs_path;
433 
434     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
435         return NULL;
436     sprintf(path, "%s/%i/cred", procfs_path, pid);
437     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
438         return NULL;
439     return Py_BuildValue("iiiiii",
440                          info.pr_ruid, info.pr_euid, info.pr_suid,
441                          info.pr_rgid, info.pr_egid, info.pr_sgid);
442 }
443 
444 
445 /*
446  * Return process voluntary and involuntary context switches as a Python tuple.
447  */
448 static PyObject *
psutil_proc_num_ctx_switches(PyObject * self,PyObject * args)449 psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) {
450     int pid;
451     char path[1000];
452     prusage_t info;
453     const char *procfs_path;
454 
455     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
456         return NULL;
457     sprintf(path, "%s/%i/usage", procfs_path, pid);
458     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
459         return NULL;
460     return Py_BuildValue("kk", info.pr_vctx, info.pr_ictx);
461 }
462 
463 
464 /*
465  * Process IO counters.
466  *
467  * Commented out and left here as a reminder.  Apparently we cannot
468  * retrieve process IO stats because:
469  * - 'pr_ioch' is a sum of chars read and written, with no distinction
470  * - 'pr_inblk' and 'pr_oublk', which should be the number of bytes
471  *    read and written, hardly increase and according to:
472  *    http://www.brendangregg.com/Solaris/paper_diskubyp1.pdf
473  *    ...they should be meaningless anyway.
474  *
475 static PyObject*
476 proc_io_counters(PyObject* self, PyObject* args) {
477     int pid;
478     char path[1000];
479     prusage_t info;
480     const char *procfs_path;
481 
482     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
483         return NULL;
484     sprintf(path, "%s/%i/usage", procfs_path, pid);
485     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
486         return NULL;
487 
488     // On Solaris we only have 'pr_ioch' which accounts for bytes read
489     // *and* written.
490     // 'pr_inblk' and 'pr_oublk' should be expressed in blocks of
491     // 8KB according to:
492     // http://www.brendangregg.com/Solaris/paper_diskubyp1.pdf (pag. 8)
493     return Py_BuildValue("kkkk",
494                          info.pr_ioch,
495                          info.pr_ioch,
496                          info.pr_inblk,
497                          info.pr_oublk);
498 }
499 */
500 
501 
502 /*
503  * Return information about a given process thread.
504  */
505 static PyObject *
psutil_proc_query_thread(PyObject * self,PyObject * args)506 psutil_proc_query_thread(PyObject *self, PyObject *args) {
507     int pid, tid;
508     char path[1000];
509     lwpstatus_t info;
510     const char *procfs_path;
511 
512     if (! PyArg_ParseTuple(args, "iis", &pid, &tid, &procfs_path))
513         return NULL;
514     sprintf(path, "%s/%i/lwp/%i/lwpstatus", procfs_path, pid, tid);
515     if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
516         return NULL;
517     return Py_BuildValue("dd",
518                          PSUTIL_TV2DOUBLE(info.pr_utime),
519                          PSUTIL_TV2DOUBLE(info.pr_stime));
520 }
521 
522 
523 /*
524  * Return information about system virtual memory.
525  */
526 static PyObject *
psutil_swap_mem(PyObject * self,PyObject * args)527 psutil_swap_mem(PyObject *self, PyObject *args) {
528 // XXX (arghhh!)
529 // total/free swap mem: commented out as for some reason I can't
530 // manage to get the same results shown by "swap -l", despite the
531 // code below is exactly the same as:
532 // http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/
533 //    cmd/swap/swap.c
534 // We're going to parse "swap -l" output from Python (sigh!)
535 
536 /*
537     struct swaptable     *st;
538     struct swapent    *swapent;
539     int    i;
540     struct stat64 statbuf;
541     char *path;
542     char fullpath[MAXPATHLEN+1];
543     int    num;
544 
545     if ((num = swapctl(SC_GETNSWP, NULL)) == -1) {
546         PyErr_SetFromErrno(PyExc_OSError);
547         return NULL;
548     }
549     if (num == 0) {
550         PyErr_SetString(PyExc_RuntimeError, "no swap devices configured");
551         return NULL;
552     }
553     if ((st = malloc(num * sizeof(swapent_t) + sizeof (int))) == NULL) {
554         PyErr_SetString(PyExc_RuntimeError, "malloc failed");
555         return NULL;
556     }
557     if ((path = malloc(num * MAXPATHLEN)) == NULL) {
558         PyErr_SetString(PyExc_RuntimeError, "malloc failed");
559         return NULL;
560     }
561     swapent = st->swt_ent;
562     for (i = 0; i < num; i++, swapent++) {
563         swapent->ste_path = path;
564         path += MAXPATHLEN;
565     }
566     st->swt_n = num;
567     if ((num = swapctl(SC_LIST, st)) == -1) {
568         PyErr_SetFromErrno(PyExc_OSError);
569         return NULL;
570     }
571 
572     swapent = st->swt_ent;
573     long t = 0, f = 0;
574     for (i = 0; i < num; i++, swapent++) {
575         int diskblks_per_page =(int)(sysconf(_SC_PAGESIZE) >> DEV_BSHIFT);
576         t += (long)swapent->ste_pages;
577         f += (long)swapent->ste_free;
578     }
579 
580     free(st);
581     return Py_BuildValue("(kk)", t, f);
582 */
583 
584     kstat_ctl_t *kc;
585     kstat_t     *k;
586     cpu_stat_t  *cpu;
587     int         cpu_count = 0;
588     int         flag = 0;
589     uint_t      sin = 0;
590     uint_t      sout = 0;
591 
592     kc = kstat_open();
593     if (kc == NULL)
594         return PyErr_SetFromErrno(PyExc_OSError);;
595 
596     k = kc->kc_chain;
597     while (k != NULL) {
598         if ((strncmp(k->ks_name, "cpu_stat", 8) == 0) && \
599                 (kstat_read(kc, k, NULL) != -1) )
600         {
601             flag = 1;
602             cpu = (cpu_stat_t *) k->ks_data;
603             sin += cpu->cpu_vminfo.pgswapin;    // num pages swapped in
604             sout += cpu->cpu_vminfo.pgswapout;  // num pages swapped out
605         }
606         cpu_count += 1;
607         k = k->ks_next;
608     }
609     kstat_close(kc);
610     if (!flag) {
611         PyErr_SetString(PyExc_RuntimeError, "no swap device was found");
612         return NULL;
613     }
614     return Py_BuildValue("(II)", sin, sout);
615 }
616 
617 
618 /*
619  * Return users currently connected on the system.
620  */
621 static PyObject *
psutil_users(PyObject * self,PyObject * args)622 psutil_users(PyObject *self, PyObject *args) {
623     struct utmpx *ut;
624     PyObject *py_tuple = NULL;
625     PyObject *py_username = NULL;
626     PyObject *py_tty = NULL;
627     PyObject *py_hostname = NULL;
628     PyObject *py_user_proc = NULL;
629     PyObject *py_retlist = PyList_New(0);
630 
631     if (py_retlist == NULL)
632         return NULL;
633 
634     setutxent();
635     while (NULL != (ut = getutxent())) {
636         if (ut->ut_type == USER_PROCESS)
637             py_user_proc = Py_True;
638         else
639             py_user_proc = Py_False;
640         py_username = PyUnicode_DecodeFSDefault(ut->ut_user);
641         if (! py_username)
642             goto error;
643         py_tty = PyUnicode_DecodeFSDefault(ut->ut_line);
644         if (! py_tty)
645             goto error;
646         py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host);
647         if (! py_hostname)
648             goto error;
649         py_tuple = Py_BuildValue(
650             "(OOOfOi)",
651             py_username,              // username
652             py_tty,                   // tty
653             py_hostname,              // hostname
654             (float)ut->ut_tv.tv_sec,  // tstamp
655             py_user_proc,             // (bool) user process
656             ut->ut_pid                // process id
657         );
658         if (py_tuple == NULL)
659             goto error;
660         if (PyList_Append(py_retlist, py_tuple))
661             goto error;
662         Py_CLEAR(py_username);
663         Py_CLEAR(py_tty);
664         Py_CLEAR(py_hostname);
665         Py_CLEAR(py_tuple);
666     }
667     endutxent();
668 
669     return py_retlist;
670 
671 error:
672     Py_XDECREF(py_username);
673     Py_XDECREF(py_tty);
674     Py_XDECREF(py_hostname);
675     Py_XDECREF(py_tuple);
676     Py_DECREF(py_retlist);
677     endutxent();
678     return NULL;
679 }
680 
681 
682 /*
683  * Return disk mounted partitions as a list of tuples including device,
684  * mount point and filesystem type.
685  */
686 static PyObject *
psutil_disk_partitions(PyObject * self,PyObject * args)687 psutil_disk_partitions(PyObject *self, PyObject *args) {
688     FILE *file;
689     struct mnttab mt;
690     PyObject *py_dev = NULL;
691     PyObject *py_mountp = NULL;
692     PyObject *py_tuple = NULL;
693     PyObject *py_retlist = PyList_New(0);
694 
695     if (py_retlist == NULL)
696         return NULL;
697 
698     file = fopen(MNTTAB, "rb");
699     if (file == NULL) {
700         PyErr_SetFromErrno(PyExc_OSError);
701         goto error;
702     }
703 
704     while (getmntent(file, &mt) == 0) {
705         py_dev = PyUnicode_DecodeFSDefault(mt.mnt_special);
706         if (! py_dev)
707             goto error;
708         py_mountp = PyUnicode_DecodeFSDefault(mt.mnt_mountp);
709         if (! py_mountp)
710             goto error;
711         py_tuple = Py_BuildValue(
712             "(OOss)",
713             py_dev,           // device
714             py_mountp,        // mount point
715             mt.mnt_fstype,    // fs type
716             mt.mnt_mntopts);  // options
717         if (py_tuple == NULL)
718             goto error;
719         if (PyList_Append(py_retlist, py_tuple))
720             goto error;
721         Py_CLEAR(py_dev);
722         Py_CLEAR(py_mountp);
723         Py_CLEAR(py_tuple);
724     }
725     fclose(file);
726     return py_retlist;
727 
728 error:
729     Py_XDECREF(py_dev);
730     Py_XDECREF(py_mountp);
731     Py_XDECREF(py_tuple);
732     Py_DECREF(py_retlist);
733     if (file != NULL)
734         fclose(file);
735     return NULL;
736 }
737 
738 
739 /*
740  * Return system-wide CPU times.
741  */
742 static PyObject *
psutil_per_cpu_times(PyObject * self,PyObject * args)743 psutil_per_cpu_times(PyObject *self, PyObject *args) {
744     kstat_ctl_t *kc;
745     kstat_t *ksp;
746     cpu_stat_t cs;
747     PyObject *py_retlist = PyList_New(0);
748     PyObject *py_cputime = NULL;
749 
750     if (py_retlist == NULL)
751         return NULL;
752 
753     kc = kstat_open();
754     if (kc == NULL) {
755         PyErr_SetFromErrno(PyExc_OSError);
756         goto error;
757     }
758 
759     for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
760         if (strcmp(ksp->ks_module, "cpu_stat") == 0) {
761             if (kstat_read(kc, ksp, &cs) == -1) {
762                 PyErr_SetFromErrno(PyExc_OSError);
763                 goto error;
764             }
765             py_cputime = Py_BuildValue("ffff",
766                                        (float)cs.cpu_sysinfo.cpu[CPU_USER],
767                                        (float)cs.cpu_sysinfo.cpu[CPU_KERNEL],
768                                        (float)cs.cpu_sysinfo.cpu[CPU_IDLE],
769                                        (float)cs.cpu_sysinfo.cpu[CPU_WAIT]);
770             if (py_cputime == NULL)
771                 goto error;
772             if (PyList_Append(py_retlist, py_cputime))
773                 goto error;
774             Py_CLEAR(py_cputime);
775         }
776     }
777 
778     kstat_close(kc);
779     return py_retlist;
780 
781 error:
782     Py_XDECREF(py_cputime);
783     Py_DECREF(py_retlist);
784     if (kc != NULL)
785         kstat_close(kc);
786     return NULL;
787 }
788 
789 
790 /*
791  * Return disk IO statistics.
792  */
793 static PyObject *
psutil_disk_io_counters(PyObject * self,PyObject * args)794 psutil_disk_io_counters(PyObject *self, PyObject *args) {
795     kstat_ctl_t *kc;
796     kstat_t *ksp;
797     kstat_io_t kio;
798     PyObject *py_retdict = PyDict_New();
799     PyObject *py_disk_info = NULL;
800 
801     if (py_retdict == NULL)
802         return NULL;
803     kc = kstat_open();
804     if (kc == NULL) {
805         PyErr_SetFromErrno(PyExc_OSError);;
806         goto error;
807     }
808     ksp = kc->kc_chain;
809     while (ksp != NULL) {
810         if (ksp->ks_type == KSTAT_TYPE_IO) {
811             if (strcmp(ksp->ks_class, "disk") == 0) {
812                 if (kstat_read(kc, ksp, &kio) == -1) {
813                     kstat_close(kc);
814                     return PyErr_SetFromErrno(PyExc_OSError);;
815                 }
816                 py_disk_info = Py_BuildValue(
817                     "(IIKKLL)",
818                     kio.reads,
819                     kio.writes,
820                     kio.nread,
821                     kio.nwritten,
822                     kio.rtime / 1000 / 1000,  // from nano to milli secs
823                     kio.wtime / 1000 / 1000   // from nano to milli secs
824                 );
825                 if (!py_disk_info)
826                     goto error;
827                 if (PyDict_SetItemString(py_retdict, ksp->ks_name,
828                                          py_disk_info))
829                     goto error;
830                 Py_CLEAR(py_disk_info);
831             }
832         }
833         ksp = ksp->ks_next;
834     }
835     kstat_close(kc);
836 
837     return py_retdict;
838 
839 error:
840     Py_XDECREF(py_disk_info);
841     Py_DECREF(py_retdict);
842     if (kc != NULL)
843         kstat_close(kc);
844     return NULL;
845 }
846 
847 
848 /*
849  * Return process memory mappings.
850  */
851 static PyObject *
psutil_proc_memory_maps(PyObject * self,PyObject * args)852 psutil_proc_memory_maps(PyObject *self, PyObject *args) {
853     int pid;
854     int fd = -1;
855     char path[1000];
856     char perms[10];
857     const char *name;
858     struct stat st;
859     pstatus_t status;
860 
861     prxmap_t *xmap = NULL, *p;
862     off_t size;
863     size_t nread;
864     int nmap;
865     uintptr_t pr_addr_sz;
866     uintptr_t stk_base_sz, brk_base_sz;
867     const char *procfs_path;
868 
869     PyObject *py_tuple = NULL;
870     PyObject *py_path = NULL;
871     PyObject *py_retlist = PyList_New(0);
872 
873     if (py_retlist == NULL)
874         return NULL;
875     if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
876         goto error;
877 
878     sprintf(path, "%s/%i/status", procfs_path, pid);
879     if (! psutil_file_to_struct(path, (void *)&status, sizeof(status)))
880         goto error;
881 
882     sprintf(path, "%s/%i/xmap", procfs_path, pid);
883     if (stat(path, &st) == -1) {
884         PyErr_SetFromErrno(PyExc_OSError);
885         goto error;
886     }
887 
888     size = st.st_size;
889 
890     fd = open(path, O_RDONLY);
891     if (fd == -1) {
892         PyErr_SetFromErrno(PyExc_OSError);
893         goto error;
894     }
895 
896     xmap = (prxmap_t *)malloc(size);
897     if (xmap == NULL) {
898         PyErr_NoMemory();
899         goto error;
900     }
901 
902     nread = pread(fd, xmap, size, 0);
903     nmap = nread / sizeof(prxmap_t);
904     p = xmap;
905 
906     while (nmap) {
907         nmap -= 1;
908         if (p == NULL) {
909             p += 1;
910             continue;
911         }
912 
913         perms[0] = '\0';
914         pr_addr_sz = p->pr_vaddr + p->pr_size;
915 
916         // perms
917         sprintf(perms, "%c%c%c%c", p->pr_mflags & MA_READ ? 'r' : '-',
918                 p->pr_mflags & MA_WRITE ? 'w' : '-',
919                 p->pr_mflags & MA_EXEC ? 'x' : '-',
920                 p->pr_mflags & MA_SHARED ? 's' : '-');
921 
922         // name
923         if (strlen(p->pr_mapname) > 0) {
924             name = p->pr_mapname;
925         }
926         else {
927             if ((p->pr_mflags & MA_ISM) || (p->pr_mflags & MA_SHM)) {
928                 name = "[shmid]";
929             }
930             else {
931                 stk_base_sz = status.pr_stkbase + status.pr_stksize;
932                 brk_base_sz = status.pr_brkbase + status.pr_brksize;
933 
934                 if ((pr_addr_sz > status.pr_stkbase) &&
935                         (p->pr_vaddr < stk_base_sz)) {
936                     name = "[stack]";
937                 }
938                 else if ((p->pr_mflags & MA_ANON) && \
939                          (pr_addr_sz > status.pr_brkbase) && \
940                          (p->pr_vaddr < brk_base_sz)) {
941                     name = "[heap]";
942                 }
943                 else {
944                     name = "[anon]";
945                 }
946             }
947         }
948 
949         py_path = PyUnicode_DecodeFSDefault(name);
950         if (! py_path)
951             goto error;
952         py_tuple = Py_BuildValue(
953             "kksOkkk",
954             (unsigned long)p->pr_vaddr,
955             (unsigned long)pr_addr_sz,
956             perms,
957             py_path,
958             (unsigned long)p->pr_rss * p->pr_pagesize,
959             (unsigned long)p->pr_anon * p->pr_pagesize,
960             (unsigned long)p->pr_locked * p->pr_pagesize);
961         if (!py_tuple)
962             goto error;
963         if (PyList_Append(py_retlist, py_tuple))
964             goto error;
965         Py_CLEAR(py_path);
966         Py_CLEAR(py_tuple);
967 
968         // increment pointer
969         p += 1;
970     }
971 
972     close(fd);
973     free(xmap);
974     return py_retlist;
975 
976 error:
977     if (fd != -1)
978         close(fd);
979     Py_XDECREF(py_tuple);
980     Py_XDECREF(py_path);
981     Py_DECREF(py_retlist);
982     if (xmap != NULL)
983         free(xmap);
984     return NULL;
985 }
986 
987 
988 /*
989  * Return a list of tuples for network I/O statistics.
990  */
991 static PyObject *
psutil_net_io_counters(PyObject * self,PyObject * args)992 psutil_net_io_counters(PyObject *self, PyObject *args) {
993     kstat_ctl_t    *kc = NULL;
994     kstat_t *ksp;
995     kstat_named_t *rbytes, *wbytes, *rpkts, *wpkts, *ierrs, *oerrs;
996     int ret;
997     int sock = -1;
998     struct lifreq ifr;
999 
1000     PyObject *py_retdict = PyDict_New();
1001     PyObject *py_ifc_info = NULL;
1002 
1003     if (py_retdict == NULL)
1004         return NULL;
1005     kc = kstat_open();
1006     if (kc == NULL)
1007         goto error;
1008 
1009     sock = socket(AF_INET, SOCK_DGRAM, 0);
1010     if (sock == -1) {
1011         PyErr_SetFromErrno(PyExc_OSError);
1012         goto error;
1013     }
1014 
1015     ksp = kc->kc_chain;
1016     while (ksp != NULL) {
1017         if (ksp->ks_type != KSTAT_TYPE_NAMED)
1018             goto next;
1019         if (strcmp(ksp->ks_class, "net") != 0)
1020             goto next;
1021         // skip 'lo' (localhost) because it doesn't have the statistics we need
1022         // and it makes kstat_data_lookup() fail
1023         if (strcmp(ksp->ks_module, "lo") == 0)
1024             goto next;
1025 
1026         // check if this is a network interface by sending a ioctl
1027         PSUTIL_STRNCPY(ifr.lifr_name, ksp->ks_name, sizeof(ifr.lifr_name));
1028         ret = ioctl(sock, SIOCGLIFFLAGS, &ifr);
1029         if (ret == -1)
1030             goto next;
1031 
1032         if (kstat_read(kc, ksp, NULL) == -1) {
1033             errno = 0;
1034             goto next;
1035         }
1036 
1037         rbytes = (kstat_named_t *)kstat_data_lookup(ksp, "rbytes");
1038         wbytes = (kstat_named_t *)kstat_data_lookup(ksp, "obytes");
1039         rpkts = (kstat_named_t *)kstat_data_lookup(ksp, "ipackets");
1040         wpkts = (kstat_named_t *)kstat_data_lookup(ksp, "opackets");
1041         ierrs = (kstat_named_t *)kstat_data_lookup(ksp, "ierrors");
1042         oerrs = (kstat_named_t *)kstat_data_lookup(ksp, "oerrors");
1043 
1044         if ((rbytes == NULL) || (wbytes == NULL) || (rpkts == NULL) ||
1045                 (wpkts == NULL) || (ierrs == NULL) || (oerrs == NULL))
1046         {
1047             PyErr_SetString(PyExc_RuntimeError, "kstat_data_lookup() failed");
1048             goto error;
1049         }
1050 
1051         if (rbytes->data_type == KSTAT_DATA_UINT64)
1052         {
1053             py_ifc_info = Py_BuildValue("(KKKKIIii)",
1054                                         wbytes->value.ui64,
1055                                         rbytes->value.ui64,
1056                                         wpkts->value.ui64,
1057                                         rpkts->value.ui64,
1058                                         ierrs->value.ui32,
1059                                         oerrs->value.ui32,
1060                                         0,  // dropin not supported
1061                                         0   // dropout not supported
1062                                        );
1063         }
1064         else
1065         {
1066             py_ifc_info = Py_BuildValue("(IIIIIIii)",
1067                                         wbytes->value.ui32,
1068                                         rbytes->value.ui32,
1069                                         wpkts->value.ui32,
1070                                         rpkts->value.ui32,
1071                                         ierrs->value.ui32,
1072                                         oerrs->value.ui32,
1073                                         0,  // dropin not supported
1074                                         0   // dropout not supported
1075                                        );
1076         }
1077         if (!py_ifc_info)
1078             goto error;
1079         if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info))
1080             goto error;
1081         Py_CLEAR(py_ifc_info);
1082         goto next;
1083 
1084 next:
1085         ksp = ksp->ks_next;
1086     }
1087 
1088     kstat_close(kc);
1089     close(sock);
1090     return py_retdict;
1091 
1092 error:
1093     Py_XDECREF(py_ifc_info);
1094     Py_DECREF(py_retdict);
1095     if (kc != NULL)
1096         kstat_close(kc);
1097     if (sock != -1) {
1098         close(sock);
1099     }
1100     return NULL;
1101 }
1102 
1103 
1104 /*
1105  * Return TCP and UDP connections opened by process.
1106  * UNIX sockets are excluded.
1107  *
1108  * Thanks to:
1109  * https://github.com/DavidGriffith/finx/blob/master/
1110  *     nxsensor-3.5.0-1/src/sysdeps/solaris.c
1111  * ...and:
1112  * https://hg.java.net/hg/solaris~on-src/file/tip/usr/src/cmd/
1113  *     cmd-inet/usr.bin/netstat/netstat.c
1114  */
1115 static PyObject *
psutil_net_connections(PyObject * self,PyObject * args)1116 psutil_net_connections(PyObject *self, PyObject *args) {
1117     long pid;
1118     int sd = 0;
1119     mib2_tcpConnEntry_t tp;
1120     mib2_udpEntry_t     ude;
1121 #if defined(AF_INET6)
1122     mib2_tcp6ConnEntry_t tp6;
1123     mib2_udp6Entry_t     ude6;
1124 #endif
1125     char buf[512];
1126     int i, flags, getcode, num_ent, state, ret;
1127     char lip[INET6_ADDRSTRLEN], rip[INET6_ADDRSTRLEN];
1128     int lport, rport;
1129     int processed_pid;
1130     int databuf_init = 0;
1131     struct strbuf ctlbuf, databuf;
1132     struct T_optmgmt_req tor = {0};
1133     struct T_optmgmt_ack toa = {0};
1134     struct T_error_ack   tea = {0};
1135     struct opthdr        mibhdr = {0};
1136 
1137     PyObject *py_retlist = PyList_New(0);
1138     PyObject *py_tuple = NULL;
1139     PyObject *py_laddr = NULL;
1140     PyObject *py_raddr = NULL;
1141 
1142     if (py_retlist == NULL)
1143         return NULL;
1144     if (! PyArg_ParseTuple(args, "l", &pid))
1145         goto error;
1146 
1147     sd = open("/dev/arp", O_RDWR);
1148     if (sd == -1) {
1149         PyErr_SetFromErrnoWithFilename(PyExc_OSError, "/dev/arp");
1150         goto error;
1151     }
1152 
1153     ret = ioctl(sd, I_PUSH, "tcp");
1154     if (ret == -1) {
1155         PyErr_SetFromErrno(PyExc_OSError);
1156         goto error;
1157     }
1158     ret = ioctl(sd, I_PUSH, "udp");
1159     if (ret == -1) {
1160         PyErr_SetFromErrno(PyExc_OSError);
1161         goto error;
1162     }
1163     //
1164     // OK, this mess is basically copied and pasted from nxsensor project
1165     // which copied and pasted it from netstat source code, mibget()
1166     // function.  Also see:
1167     // http://stackoverflow.com/questions/8723598/
1168     tor.PRIM_type = T_SVR4_OPTMGMT_REQ;
1169     tor.OPT_offset = sizeof (struct T_optmgmt_req);
1170     tor.OPT_length = sizeof (struct opthdr);
1171     tor.MGMT_flags = T_CURRENT;
1172     mibhdr.level = MIB2_IP;
1173     mibhdr.name  = 0;
1174 
1175 #ifdef NEW_MIB_COMPLIANT
1176     mibhdr.len   = 1;
1177 #else
1178     mibhdr.len   = 0;
1179 #endif
1180     memcpy(buf, &tor, sizeof tor);
1181     memcpy(buf + tor.OPT_offset, &mibhdr, sizeof mibhdr);
1182 
1183     ctlbuf.buf = buf;
1184     ctlbuf.len = tor.OPT_offset + tor.OPT_length;
1185     flags = 0;  // request to be sent in non-priority
1186 
1187     if (putmsg(sd, &ctlbuf, (struct strbuf *)0, flags) == -1) {
1188         PyErr_SetFromErrno(PyExc_OSError);
1189         goto error;
1190     }
1191 
1192     ctlbuf.maxlen = sizeof (buf);
1193     for (;;) {
1194         flags = 0;
1195         getcode = getmsg(sd, &ctlbuf, (struct strbuf *)0, &flags);
1196         memcpy(&toa, buf, sizeof toa);
1197         memcpy(&tea, buf, sizeof tea);
1198 
1199         if (getcode != MOREDATA ||
1200                 ctlbuf.len < (int)sizeof (struct T_optmgmt_ack) ||
1201                 toa.PRIM_type != T_OPTMGMT_ACK ||
1202                 toa.MGMT_flags != T_SUCCESS)
1203         {
1204             break;
1205         }
1206         if (ctlbuf.len >= (int)sizeof (struct T_error_ack) &&
1207                 tea.PRIM_type == T_ERROR_ACK)
1208         {
1209             PyErr_SetString(PyExc_RuntimeError, "ERROR_ACK");
1210             goto error;
1211         }
1212         if (getcode == 0 &&
1213                 ctlbuf.len >= (int)sizeof (struct T_optmgmt_ack) &&
1214                 toa.PRIM_type == T_OPTMGMT_ACK &&
1215                 toa.MGMT_flags == T_SUCCESS)
1216         {
1217             PyErr_SetString(PyExc_RuntimeError, "ERROR_T_OPTMGMT_ACK");
1218             goto error;
1219         }
1220 
1221         memset(&mibhdr, 0x0, sizeof(mibhdr));
1222         memcpy(&mibhdr, buf + toa.OPT_offset, toa.OPT_length);
1223 
1224         databuf.maxlen = mibhdr.len;
1225         databuf.len = 0;
1226         databuf.buf = (char *)malloc((int)mibhdr.len);
1227         if (!databuf.buf) {
1228             PyErr_NoMemory();
1229             goto error;
1230         }
1231         databuf_init = 1;
1232 
1233         flags = 0;
1234         getcode = getmsg(sd, (struct strbuf *)0, &databuf, &flags);
1235         if (getcode < 0) {
1236             PyErr_SetFromErrno(PyExc_OSError);
1237             goto error;
1238         }
1239 
1240         // TCPv4
1241         if (mibhdr.level == MIB2_TCP && mibhdr.name == MIB2_TCP_13) {
1242             num_ent = mibhdr.len / sizeof(mib2_tcpConnEntry_t);
1243             for (i = 0; i < num_ent; i++) {
1244                 memcpy(&tp, databuf.buf + i * sizeof tp, sizeof tp);
1245 #ifdef NEW_MIB_COMPLIANT
1246                 processed_pid = tp.tcpConnCreationProcess;
1247 #else
1248                 processed_pid = 0;
1249 #endif
1250                 if (pid != -1 && processed_pid != pid)
1251                     continue;
1252                 // construct local/remote addresses
1253                 inet_ntop(AF_INET, &tp.tcpConnLocalAddress, lip, sizeof(lip));
1254                 inet_ntop(AF_INET, &tp.tcpConnRemAddress, rip, sizeof(rip));
1255                 lport = tp.tcpConnLocalPort;
1256                 rport = tp.tcpConnRemPort;
1257 
1258                 // contruct python tuple/list
1259                 py_laddr = Py_BuildValue("(si)", lip, lport);
1260                 if (!py_laddr)
1261                     goto error;
1262                 if (rport != 0)
1263                     py_raddr = Py_BuildValue("(si)", rip, rport);
1264                 else {
1265                     py_raddr = Py_BuildValue("()");
1266                 }
1267                 if (!py_raddr)
1268                     goto error;
1269                 state = tp.tcpConnEntryInfo.ce_state;
1270 
1271                 // add item
1272                 py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET, SOCK_STREAM,
1273                                          py_laddr, py_raddr, state,
1274                                          processed_pid);
1275                 if (!py_tuple)
1276                     goto error;
1277                 if (PyList_Append(py_retlist, py_tuple))
1278                     goto error;
1279                 Py_CLEAR(py_tuple);
1280             }
1281         }
1282 #if defined(AF_INET6)
1283         // TCPv6
1284         else if (mibhdr.level == MIB2_TCP6 && mibhdr.name == MIB2_TCP6_CONN)
1285         {
1286             num_ent = mibhdr.len / sizeof(mib2_tcp6ConnEntry_t);
1287 
1288             for (i = 0; i < num_ent; i++) {
1289                 memcpy(&tp6, databuf.buf + i * sizeof tp6, sizeof tp6);
1290 #ifdef NEW_MIB_COMPLIANT
1291                 processed_pid = tp6.tcp6ConnCreationProcess;
1292 #else
1293                         processed_pid = 0;
1294 #endif
1295                 if (pid != -1 && processed_pid != pid)
1296                     continue;
1297                 // construct local/remote addresses
1298                 inet_ntop(AF_INET6, &tp6.tcp6ConnLocalAddress, lip, sizeof(lip));
1299                 inet_ntop(AF_INET6, &tp6.tcp6ConnRemAddress, rip, sizeof(rip));
1300                 lport = tp6.tcp6ConnLocalPort;
1301                 rport = tp6.tcp6ConnRemPort;
1302 
1303                 // contruct python tuple/list
1304                 py_laddr = Py_BuildValue("(si)", lip, lport);
1305                 if (!py_laddr)
1306                     goto error;
1307                 if (rport != 0)
1308                     py_raddr = Py_BuildValue("(si)", rip, rport);
1309                 else
1310                     py_raddr = Py_BuildValue("()");
1311                 if (!py_raddr)
1312                     goto error;
1313                 state = tp6.tcp6ConnEntryInfo.ce_state;
1314 
1315                 // add item
1316                 py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET6, SOCK_STREAM,
1317                                          py_laddr, py_raddr, state, processed_pid);
1318                 if (!py_tuple)
1319                     goto error;
1320                 if (PyList_Append(py_retlist, py_tuple))
1321                     goto error;
1322                 Py_CLEAR(py_tuple);
1323             }
1324         }
1325 #endif
1326         // UDPv4
1327         else if (mibhdr.level == MIB2_UDP || mibhdr.level == MIB2_UDP_ENTRY) {
1328             num_ent = mibhdr.len / sizeof(mib2_udpEntry_t);
1329             assert(num_ent * sizeof(mib2_udpEntry_t) == mibhdr.len);
1330             for (i = 0; i < num_ent; i++) {
1331                 memcpy(&ude, databuf.buf + i * sizeof ude, sizeof ude);
1332 #ifdef NEW_MIB_COMPLIANT
1333                 processed_pid = ude.udpCreationProcess;
1334 #else
1335                 processed_pid = 0;
1336 #endif
1337                 if (pid != -1 && processed_pid != pid)
1338                     continue;
1339                 // XXX Very ugly hack! It seems we get here only the first
1340                 // time we bump into a UDPv4 socket.  PID is a very high
1341                 // number (clearly impossible) and the address does not
1342                 // belong to any valid interface.  Not sure what else
1343                 // to do other than skipping.
1344                 if (processed_pid > 131072)
1345                     continue;
1346                 inet_ntop(AF_INET, &ude.udpLocalAddress, lip, sizeof(lip));
1347                 lport = ude.udpLocalPort;
1348                 py_laddr = Py_BuildValue("(si)", lip, lport);
1349                 if (!py_laddr)
1350                     goto error;
1351                 py_raddr = Py_BuildValue("()");
1352                 if (!py_raddr)
1353                     goto error;
1354                 py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET, SOCK_DGRAM,
1355                                          py_laddr, py_raddr, PSUTIL_CONN_NONE,
1356                                          processed_pid);
1357                 if (!py_tuple)
1358                     goto error;
1359                 if (PyList_Append(py_retlist, py_tuple))
1360                     goto error;
1361                 Py_CLEAR(py_tuple);
1362             }
1363         }
1364 #if defined(AF_INET6)
1365         // UDPv6
1366         else if (mibhdr.level == MIB2_UDP6 ||
1367                     mibhdr.level == MIB2_UDP6_ENTRY)
1368             {
1369             num_ent = mibhdr.len / sizeof(mib2_udp6Entry_t);
1370             for (i = 0; i < num_ent; i++) {
1371                 memcpy(&ude6, databuf.buf + i * sizeof ude6, sizeof ude6);
1372 #ifdef NEW_MIB_COMPLIANT
1373                 processed_pid = ude6.udp6CreationProcess;
1374 #else
1375                 processed_pid = 0;
1376 #endif
1377                 if (pid != -1 && processed_pid != pid)
1378                     continue;
1379                 inet_ntop(AF_INET6, &ude6.udp6LocalAddress, lip, sizeof(lip));
1380                 lport = ude6.udp6LocalPort;
1381                 py_laddr = Py_BuildValue("(si)", lip, lport);
1382                 if (!py_laddr)
1383                     goto error;
1384                 py_raddr = Py_BuildValue("()");
1385                 if (!py_raddr)
1386                     goto error;
1387                 py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET6, SOCK_DGRAM,
1388                                          py_laddr, py_raddr, PSUTIL_CONN_NONE,
1389                                          processed_pid);
1390                 if (!py_tuple)
1391                     goto error;
1392                 if (PyList_Append(py_retlist, py_tuple))
1393                     goto error;
1394                 Py_CLEAR(py_tuple);
1395             }
1396         }
1397 #endif
1398         free(databuf.buf);
1399     }
1400 
1401     close(sd);
1402     return py_retlist;
1403 
1404 error:
1405     Py_XDECREF(py_tuple);
1406     Py_XDECREF(py_laddr);
1407     Py_XDECREF(py_raddr);
1408     Py_DECREF(py_retlist);
1409     if (databuf_init == 1)
1410         free(databuf.buf);
1411     if (sd != 0)
1412         close(sd);
1413     return NULL;
1414 }
1415 
1416 
1417 static PyObject *
psutil_boot_time(PyObject * self,PyObject * args)1418 psutil_boot_time(PyObject *self, PyObject *args) {
1419     float boot_time = 0.0;
1420     struct utmpx *ut;
1421 
1422     setutxent();
1423     while (NULL != (ut = getutxent())) {
1424         if (ut->ut_type == BOOT_TIME) {
1425             boot_time = (float)ut->ut_tv.tv_sec;
1426             break;
1427         }
1428     }
1429     endutxent();
1430     if (fabs(boot_time) < 0.000001) {
1431         /* could not find BOOT_TIME in getutxent loop */
1432         PyErr_SetString(PyExc_RuntimeError, "can't determine boot time");
1433         return NULL;
1434     }
1435     return Py_BuildValue("f", boot_time);
1436 }
1437 
1438 
1439 /*
1440  * Return the number of physical CPU cores on the system.
1441  */
1442 static PyObject *
psutil_cpu_count_phys(PyObject * self,PyObject * args)1443 psutil_cpu_count_phys(PyObject *self, PyObject *args) {
1444     kstat_ctl_t *kc;
1445     kstat_t *ksp;
1446     int ncpus = 0;
1447 
1448     kc = kstat_open();
1449     if (kc == NULL)
1450         goto error;
1451     ksp = kstat_lookup(kc, "cpu_info", -1, NULL);
1452     if (ksp == NULL)
1453         goto error;
1454 
1455     for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
1456         if (strcmp(ksp->ks_module, "cpu_info") != 0)
1457             continue;
1458         if (kstat_read(kc, ksp, NULL) == -1)
1459             goto error;
1460         ncpus += 1;
1461     }
1462 
1463     kstat_close(kc);
1464     if (ncpus > 0)
1465         return Py_BuildValue("i", ncpus);
1466     else
1467         goto error;
1468 
1469 error:
1470     // mimic os.cpu_count()
1471     if (kc != NULL)
1472         kstat_close(kc);
1473     Py_RETURN_NONE;
1474 }
1475 
1476 
1477 /*
1478  * Return stats about a particular network
1479  * interface.  References:
1480  * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py
1481  * http://www.i-scream.org/libstatgrab/
1482  */
1483 static PyObject*
psutil_net_if_stats(PyObject * self,PyObject * args)1484 psutil_net_if_stats(PyObject* self, PyObject* args) {
1485     kstat_ctl_t *kc = NULL;
1486     kstat_t *ksp;
1487     kstat_named_t *knp;
1488     int ret;
1489     int sock = -1;
1490     int duplex;
1491     int speed;
1492 
1493     PyObject *py_retdict = PyDict_New();
1494     PyObject *py_ifc_info = NULL;
1495     PyObject *py_is_up = NULL;
1496 
1497     if (py_retdict == NULL)
1498         return NULL;
1499     kc = kstat_open();
1500     if (kc == NULL)
1501         goto error;
1502     sock = socket(AF_INET, SOCK_DGRAM, 0);
1503     if (sock == -1) {
1504         PyErr_SetFromErrno(PyExc_OSError);
1505         goto error;
1506     }
1507 
1508     for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
1509         if (strcmp(ksp->ks_class, "net") == 0) {
1510             struct lifreq ifr;
1511 
1512             kstat_read(kc, ksp, NULL);
1513             if (ksp->ks_type != KSTAT_TYPE_NAMED)
1514                 continue;
1515             if (strcmp(ksp->ks_class, "net") != 0)
1516                 continue;
1517 
1518             PSUTIL_STRNCPY(ifr.lifr_name, ksp->ks_name, sizeof(ifr.lifr_name));
1519             ret = ioctl(sock, SIOCGLIFFLAGS, &ifr);
1520             if (ret == -1)
1521                 continue;  // not a network interface
1522 
1523             // is up?
1524             if ((ifr.lifr_flags & IFF_UP) != 0) {
1525                 if ((knp = kstat_data_lookup(ksp, "link_up")) != NULL) {
1526                     if (knp->value.ui32 != 0u)
1527                         py_is_up = Py_True;
1528                     else
1529                         py_is_up = Py_False;
1530                 }
1531                 else {
1532                     py_is_up = Py_True;
1533                 }
1534             }
1535             else {
1536                 py_is_up = Py_False;
1537             }
1538             Py_INCREF(py_is_up);
1539 
1540             // duplex
1541             duplex = 0;  // unknown
1542             if ((knp = kstat_data_lookup(ksp, "link_duplex")) != NULL) {
1543                 if (knp->value.ui32 == 1)
1544                     duplex = 1;  // half
1545                 else if (knp->value.ui32 == 2)
1546                     duplex = 2;  // full
1547             }
1548 
1549             // speed
1550             if ((knp = kstat_data_lookup(ksp, "ifspeed")) != NULL)
1551                 // expressed in bits per sec, we want mega bits per sec
1552                 speed = (int)knp->value.ui64 / 1000000;
1553             else
1554                 speed = 0;
1555 
1556             // mtu
1557             ret = ioctl(sock, SIOCGLIFMTU, &ifr);
1558             if (ret == -1)
1559                 goto error;
1560 
1561             py_ifc_info = Py_BuildValue("(Oiii)", py_is_up, duplex, speed,
1562                                         ifr.lifr_mtu);
1563             if (!py_ifc_info)
1564                 goto error;
1565             if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info))
1566                 goto error;
1567             Py_CLEAR(py_ifc_info);
1568         }
1569     }
1570 
1571     close(sock);
1572     kstat_close(kc);
1573     return py_retdict;
1574 
1575 error:
1576     Py_XDECREF(py_is_up);
1577     Py_XDECREF(py_ifc_info);
1578     Py_DECREF(py_retdict);
1579     if (sock != -1)
1580         close(sock);
1581     if (kc != NULL)
1582         kstat_close(kc);
1583     PyErr_SetFromErrno(PyExc_OSError);
1584     return NULL;
1585 }
1586 
1587 
1588 /*
1589  * Return CPU statistics.
1590  */
1591 static PyObject *
psutil_cpu_stats(PyObject * self,PyObject * args)1592 psutil_cpu_stats(PyObject *self, PyObject *args) {
1593     kstat_ctl_t *kc;
1594     kstat_t *ksp;
1595     cpu_stat_t cs;
1596     unsigned int ctx_switches = 0;
1597     unsigned int interrupts = 0;
1598     unsigned int traps = 0;
1599     unsigned int syscalls = 0;
1600 
1601     kc = kstat_open();
1602     if (kc == NULL) {
1603         PyErr_SetFromErrno(PyExc_OSError);
1604         goto error;
1605     }
1606 
1607     for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
1608         if (strcmp(ksp->ks_module, "cpu_stat") == 0) {
1609             if (kstat_read(kc, ksp, &cs) == -1) {
1610                 PyErr_SetFromErrno(PyExc_OSError);
1611                 goto error;
1612             }
1613             // voluntary + involuntary
1614             ctx_switches += cs.cpu_sysinfo.pswitch + cs.cpu_sysinfo.inv_swtch;
1615             interrupts += cs.cpu_sysinfo.intr;
1616             traps += cs.cpu_sysinfo.trap;
1617             syscalls += cs.cpu_sysinfo.syscall;
1618         }
1619     }
1620 
1621     kstat_close(kc);
1622     return Py_BuildValue(
1623         "IIII", ctx_switches, interrupts, syscalls, traps);
1624 
1625 error:
1626     if (kc != NULL)
1627         kstat_close(kc);
1628     return NULL;
1629 }
1630 
1631 
1632 /*
1633  * define the psutil C module methods and initialize the module.
1634  */
1635 static PyMethodDef
1636 PsutilMethods[] = {
1637     // --- process-related functions
1638     {"proc_basic_info", psutil_proc_basic_info, METH_VARARGS,
1639      "Return process ppid, rss, vms, ctime, nice, nthreads, status and tty"},
1640     {"proc_name_and_args", psutil_proc_name_and_args, METH_VARARGS,
1641      "Return process name and args."},
1642     {"proc_environ", psutil_proc_environ, METH_VARARGS,
1643       "Return process environment."},
1644     {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS,
1645      "Return process user and system CPU times."},
1646     {"proc_cred", psutil_proc_cred, METH_VARARGS,
1647      "Return process uids/gids."},
1648     {"query_process_thread", psutil_proc_query_thread, METH_VARARGS,
1649      "Return info about a process thread"},
1650     {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS,
1651      "Return process memory mappings"},
1652     {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS,
1653      "Return the number of context switches performed by process"},
1654     {"proc_cpu_num", psutil_proc_cpu_num, METH_VARARGS,
1655      "Return what CPU the process is on"},
1656 
1657     // --- system-related functions
1658     {"swap_mem", psutil_swap_mem, METH_VARARGS,
1659      "Return information about system swap memory."},
1660     {"users", psutil_users, METH_VARARGS,
1661      "Return currently connected users."},
1662     {"disk_partitions", psutil_disk_partitions, METH_VARARGS,
1663      "Return disk partitions."},
1664     {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS,
1665      "Return system per-CPU times."},
1666     {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS,
1667      "Return a Python dict of tuples for disk I/O statistics."},
1668     {"net_io_counters", psutil_net_io_counters, METH_VARARGS,
1669      "Return a Python dict of tuples for network I/O statistics."},
1670     {"boot_time", psutil_boot_time, METH_VARARGS,
1671      "Return system boot time in seconds since the EPOCH."},
1672     {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS,
1673      "Return the number of physical CPUs on the system."},
1674     {"net_connections", psutil_net_connections, METH_VARARGS,
1675      "Return TCP and UDP syste-wide open connections."},
1676     {"net_if_stats", psutil_net_if_stats, METH_VARARGS,
1677      "Return NIC stats (isup, duplex, speed, mtu)"},
1678     {"cpu_stats", psutil_cpu_stats, METH_VARARGS,
1679      "Return CPU statistics"},
1680 
1681     // --- others
1682     {"set_testing", psutil_set_testing, METH_NOARGS,
1683      "Set psutil in testing mode"},
1684 
1685     {NULL, NULL, 0, NULL}
1686 };
1687 
1688 
1689 struct module_state {
1690     PyObject *error;
1691 };
1692 
1693 #if PY_MAJOR_VERSION >= 3
1694 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
1695 #else
1696 #define GETSTATE(m) (&_state)
1697 #endif
1698 
1699 #if PY_MAJOR_VERSION >= 3
1700 
1701 static int
psutil_sunos_traverse(PyObject * m,visitproc visit,void * arg)1702 psutil_sunos_traverse(PyObject *m, visitproc visit, void *arg) {
1703     Py_VISIT(GETSTATE(m)->error);
1704     return 0;
1705 }
1706 
1707 static int
psutil_sunos_clear(PyObject * m)1708 psutil_sunos_clear(PyObject *m) {
1709     Py_CLEAR(GETSTATE(m)->error);
1710     return 0;
1711 }
1712 
1713 static struct PyModuleDef moduledef = {
1714     PyModuleDef_HEAD_INIT,
1715     "psutil_sunos",
1716     NULL,
1717     sizeof(struct module_state),
1718     PsutilMethods,
1719     NULL,
1720     psutil_sunos_traverse,
1721     psutil_sunos_clear,
1722     NULL
1723 };
1724 
1725 #define INITERROR return NULL
1726 
PyInit__psutil_sunos(void)1727 PyMODINIT_FUNC PyInit__psutil_sunos(void)
1728 
1729 #else
1730 #define INITERROR return
1731 
1732 void init_psutil_sunos(void)
1733 #endif
1734 {
1735 #if PY_MAJOR_VERSION >= 3
1736     PyObject *module = PyModule_Create(&moduledef);
1737 #else
1738     PyObject *module = Py_InitModule("_psutil_sunos", PsutilMethods);
1739 #endif
1740     if (module == NULL)
1741         INITERROR;
1742 
1743     if (psutil_setup() != 0)
1744         INITERROR;
1745 
1746     PyModule_AddIntConstant(module, "version", PSUTIL_VERSION);
1747 
1748     PyModule_AddIntConstant(module, "SSLEEP", SSLEEP);
1749     PyModule_AddIntConstant(module, "SRUN", SRUN);
1750     PyModule_AddIntConstant(module, "SZOMB", SZOMB);
1751     PyModule_AddIntConstant(module, "SSTOP", SSTOP);
1752     PyModule_AddIntConstant(module, "SIDL", SIDL);
1753     PyModule_AddIntConstant(module, "SONPROC", SONPROC);
1754 #ifdef SWAIT
1755     PyModule_AddIntConstant(module, "SWAIT", SWAIT);
1756 #else
1757     /* sys/proc.h started defining SWAIT somewhere
1758      * after Update 3 and prior to Update 5 included.
1759      */
1760     PyModule_AddIntConstant(module, "SWAIT", 0);
1761 #endif
1762 
1763     PyModule_AddIntConstant(module, "PRNODEV", PRNODEV);  // for process tty
1764 
1765     PyModule_AddIntConstant(module, "TCPS_CLOSED", TCPS_CLOSED);
1766     PyModule_AddIntConstant(module, "TCPS_CLOSING", TCPS_CLOSING);
1767     PyModule_AddIntConstant(module, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT);
1768     PyModule_AddIntConstant(module, "TCPS_LISTEN", TCPS_LISTEN);
1769     PyModule_AddIntConstant(module, "TCPS_ESTABLISHED", TCPS_ESTABLISHED);
1770     PyModule_AddIntConstant(module, "TCPS_SYN_SENT", TCPS_SYN_SENT);
1771     PyModule_AddIntConstant(module, "TCPS_SYN_RCVD", TCPS_SYN_RCVD);
1772     PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1);
1773     PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2);
1774     PyModule_AddIntConstant(module, "TCPS_LAST_ACK", TCPS_LAST_ACK);
1775     PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT);
1776     // sunos specific
1777     PyModule_AddIntConstant(module, "TCPS_IDLE", TCPS_IDLE);
1778     // sunos specific
1779     PyModule_AddIntConstant(module, "TCPS_BOUND", TCPS_BOUND);
1780     PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);
1781 
1782     if (module == NULL)
1783         INITERROR;
1784 #if PY_MAJOR_VERSION >= 3
1785     return module;
1786 #endif
1787 }
1788