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