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