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