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