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