| /* |
| * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * Functions specific to Sun OS Solaris platforms. |
| * |
| * Thanks to Justin Venus who originally wrote a consistent part of |
| * this in Cython which I later on translated in C. |
| */ |
| |
| |
| #include <Python.h> |
| |
| // fix for "Cannot use procfs in the large file compilation environment" |
| // error, see: |
| // http://sourceware.org/ml/gdb-patches/2010-11/msg00336.html |
| #undef _FILE_OFFSET_BITS |
| #define _STRUCTURED_PROC 1 |
| |
| // fix compilation issue on SunOS 5.10, see: |
| // https://github.com/giampaolo/psutil/issues/421 |
| #define NEW_MIB_COMPLIANT |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/proc.h> |
| #include <sys/swap.h> |
| #include <sys/sysinfo.h> |
| #include <sys/mntent.h> // for MNTTAB |
| #include <sys/mnttab.h> |
| #include <sys/procfs.h> |
| #include <sys/sockio.h> |
| #include <sys/socket.h> |
| #include <fcntl.h> |
| #include <utmpx.h> |
| #include <kstat.h> |
| #include <sys/ioctl.h> |
| #include <sys/tihdr.h> |
| #include <stropts.h> |
| #include <inet/tcp.h> |
| #include <arpa/inet.h> |
| #include <net/if.h> |
| |
| #include "_psutil_sunos.h" |
| |
| |
| #define TV2DOUBLE(t) (((t).tv_nsec * 0.000000001) + (t).tv_sec) |
| |
| /* |
| * Read a file content and fills a C structure with it. |
| */ |
| int |
| psutil_file_to_struct(char *path, void *fstruct, size_t size) |
| { |
| int fd; |
| size_t nbytes; |
| fd = open(path, O_RDONLY); |
| if (fd == -1) { |
| PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); |
| return 0; |
| } |
| nbytes = read(fd, fstruct, size); |
| if (nbytes <= 0) { |
| close(fd); |
| PyErr_SetFromErrno(PyExc_OSError); |
| return 0; |
| } |
| if (nbytes != size) { |
| close(fd); |
| PyErr_SetString(PyExc_RuntimeError, "structure size mismatch"); |
| return 0; |
| } |
| close(fd); |
| return nbytes; |
| } |
| |
| |
| /* |
| * Return process ppid, rss, vms, ctime, nice, nthreads, status and tty |
| * as a Python tuple. |
| */ |
| static PyObject * |
| psutil_proc_basic_info(PyObject *self, PyObject *args) |
| { |
| int pid; |
| char path[100]; |
| psinfo_t info; |
| |
| if (! PyArg_ParseTuple(args, "i", &pid)) |
| return NULL; |
| sprintf(path, "/proc/%i/psinfo", pid); |
| if (! psutil_file_to_struct(path, (void *)&info, sizeof(info))) |
| return NULL; |
| return Py_BuildValue("ikkdiiik", |
| info.pr_ppid, // parent pid |
| info.pr_rssize, // rss |
| info.pr_size, // vms |
| TV2DOUBLE(info.pr_start), // create time |
| info.pr_lwp.pr_nice, // nice |
| info.pr_nlwp, // no. of threads |
| info.pr_lwp.pr_state, // status code |
| info.pr_ttydev // tty nr |
| ); |
| } |
| |
| |
| /* |
| * Return process name and args as a Python tuple. |
| */ |
| static PyObject * |
| psutil_proc_name_and_args(PyObject *self, PyObject *args) |
| { |
| int pid; |
| char path[100]; |
| psinfo_t info; |
| |
| if (! PyArg_ParseTuple(args, "i", &pid)) |
| return NULL; |
| sprintf(path, "/proc/%i/psinfo", pid); |
| if (! psutil_file_to_struct(path, (void *)&info, sizeof(info))) |
| return NULL; |
| return Py_BuildValue("ss", info.pr_fname, info.pr_psargs); |
| } |
| |
| |
| /* |
| * Return process user and system CPU times as a Python tuple. |
| */ |
| static PyObject * |
| psutil_proc_cpu_times(PyObject *self, PyObject *args) |
| { |
| int pid; |
| char path[100]; |
| pstatus_t info; |
| |
| if (! PyArg_ParseTuple(args, "i", &pid)) |
| return NULL; |
| sprintf(path, "/proc/%i/status", pid); |
| if (! psutil_file_to_struct(path, (void *)&info, sizeof(info))) |
| return NULL; |
| // results are more precise than os.times() |
| return Py_BuildValue("dd", |
| TV2DOUBLE(info.pr_utime), |
| TV2DOUBLE(info.pr_stime)); |
| } |
| |
| |
| /* |
| * Return process uids/gids as a Python tuple. |
| */ |
| static PyObject * |
| psutil_proc_cred(PyObject *self, PyObject *args) |
| { |
| int pid; |
| char path[100]; |
| prcred_t info; |
| |
| if (! PyArg_ParseTuple(args, "i", &pid)) |
| return NULL; |
| sprintf(path, "/proc/%i/cred", pid); |
| if (! psutil_file_to_struct(path, (void *)&info, sizeof(info))) |
| return NULL; |
| return Py_BuildValue("iiiiii", |
| info.pr_ruid, info.pr_euid, info.pr_suid, |
| info.pr_rgid, info.pr_egid, info.pr_sgid); |
| } |
| |
| |
| /* |
| * Return process uids/gids as a Python tuple. |
| */ |
| static PyObject * |
| psutil_proc_num_ctx_switches(PyObject *self, PyObject *args) |
| { |
| int pid; |
| char path[100]; |
| prusage_t info; |
| |
| if (! PyArg_ParseTuple(args, "i", &pid)) |
| return NULL; |
| sprintf(path, "/proc/%i/usage", pid); |
| if (! psutil_file_to_struct(path, (void *)&info, sizeof(info))) |
| return NULL; |
| return Py_BuildValue("kk", info.pr_vctx, info.pr_ictx); |
| } |
| |
| |
| /* |
| * Process IO counters. |
| * |
| * Commented out and left here as a reminder. Apparently we cannot |
| * retrieve process IO stats because: |
| * - 'pr_ioch' is a sum of chars read and written, with no distinction |
| * - 'pr_inblk' and 'pr_oublk', which should be the number of bytes |
| * read and written, hardly increase and according to: |
| * http://www.brendangregg.com/Perf/paper_diskubyp1.pdf |
| * ...they should be meaningless anyway. |
| * |
| static PyObject* |
| proc_io_counters(PyObject* self, PyObject* args) |
| { |
| int pid; |
| char path[100]; |
| prusage_t info; |
| |
| if (! PyArg_ParseTuple(args, "i", &pid)) |
| return NULL; |
| sprintf(path, "/proc/%i/usage", pid); |
| if (! psutil_file_to_struct(path, (void *)&info, sizeof(info))) |
| return NULL; |
| |
| // On Solaris we only have 'pr_ioch' which accounts for bytes read |
| // *and* written. |
| // 'pr_inblk' and 'pr_oublk' should be expressed in blocks of |
| // 8KB according to: |
| // http://www.brendangregg.com/Perf/paper_diskubyp1.pdf (pag. 8) |
| return Py_BuildValue("kkkk", |
| info.pr_ioch, |
| info.pr_ioch, |
| info.pr_inblk, |
| info.pr_oublk); |
| } |
| */ |
| |
| |
| /* |
| * Return information about a given process thread. |
| */ |
| static PyObject * |
| psutil_proc_query_thread(PyObject *self, PyObject *args) |
| { |
| int pid, tid; |
| char path[100]; |
| lwpstatus_t info; |
| |
| if (! PyArg_ParseTuple(args, "ii", &pid, &tid)) |
| return NULL; |
| sprintf(path, "/proc/%i/lwp/%i/lwpstatus", pid, tid); |
| if (! psutil_file_to_struct(path, (void *)&info, sizeof(info))) |
| return NULL; |
| return Py_BuildValue("dd", |
| TV2DOUBLE(info.pr_utime), |
| TV2DOUBLE(info.pr_stime)); |
| } |
| |
| |
| /* |
| * Return information about system virtual memory. |
| */ |
| static PyObject * |
| psutil_swap_mem(PyObject *self, PyObject *args) |
| { |
| // XXX (arghhh!) |
| // total/free swap mem: commented out as for some reason I can't |
| // manage to get the same results shown by "swap -l", despite the |
| // code below is exactly the same as: |
| // http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/ |
| // cmd/swap/swap.c |
| // We're going to parse "swap -l" output from Python (sigh!) |
| |
| /* |
| struct swaptable *st; |
| struct swapent *swapent; |
| int i; |
| struct stat64 statbuf; |
| char *path; |
| char fullpath[MAXPATHLEN+1]; |
| int num; |
| |
| if ((num = swapctl(SC_GETNSWP, NULL)) == -1) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| return NULL; |
| } |
| if (num == 0) { |
| PyErr_SetString(PyExc_RuntimeError, "no swap devices configured"); |
| return NULL; |
| } |
| if ((st = malloc(num * sizeof(swapent_t) + sizeof (int))) == NULL) { |
| PyErr_SetString(PyExc_RuntimeError, "malloc failed"); |
| return NULL; |
| } |
| if ((path = malloc(num * MAXPATHLEN)) == NULL) { |
| PyErr_SetString(PyExc_RuntimeError, "malloc failed"); |
| return NULL; |
| } |
| swapent = st->swt_ent; |
| for (i = 0; i < num; i++, swapent++) { |
| swapent->ste_path = path; |
| path += MAXPATHLEN; |
| } |
| st->swt_n = num; |
| if ((num = swapctl(SC_LIST, st)) == -1) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| return NULL; |
| } |
| |
| swapent = st->swt_ent; |
| long t = 0, f = 0; |
| for (i = 0; i < num; i++, swapent++) { |
| int diskblks_per_page =(int)(sysconf(_SC_PAGESIZE) >> DEV_BSHIFT); |
| t += (long)swapent->ste_pages; |
| f += (long)swapent->ste_free; |
| } |
| |
| free(st); |
| return Py_BuildValue("(kk)", t, f); |
| */ |
| |
| kstat_ctl_t *kc; |
| kstat_t *k; |
| cpu_stat_t *cpu; |
| int cpu_count = 0; |
| int flag = 0; |
| uint_t sin = 0; |
| uint_t sout = 0; |
| |
| kc = kstat_open(); |
| if (kc == NULL) |
| return PyErr_SetFromErrno(PyExc_OSError);; |
| |
| k = kc->kc_chain; |
| while (k != NULL) { |
| if ((strncmp(k->ks_name, "cpu_stat", 8) == 0) && \ |
| (kstat_read(kc, k, NULL) != -1) ) |
| { |
| flag = 1; |
| cpu = (cpu_stat_t *) k->ks_data; |
| sin += cpu->cpu_vminfo.pgswapin; // num pages swapped in |
| sout += cpu->cpu_vminfo.pgswapout; // num pages swapped out |
| } |
| cpu_count += 1; |
| k = k->ks_next; |
| } |
| kstat_close(kc); |
| if (!flag) { |
| PyErr_SetString(PyExc_RuntimeError, "no swap device was found"); |
| return NULL; |
| } |
| return Py_BuildValue("(II)", sin, sout); |
| } |
| |
| |
| /* |
| * Return users currently connected on the system. |
| */ |
| static PyObject * |
| psutil_users(PyObject *self, PyObject *args) |
| { |
| struct utmpx *ut; |
| PyObject *ret_list = PyList_New(0); |
| PyObject *tuple = NULL; |
| PyObject *user_proc = NULL; |
| |
| if (ret_list == NULL) |
| return NULL; |
| |
| while (NULL != (ut = getutxent())) { |
| if (ut->ut_type == USER_PROCESS) |
| user_proc = Py_True; |
| else |
| user_proc = Py_False; |
| tuple = Py_BuildValue( |
| "(sssfO)", |
| ut->ut_user, // username |
| ut->ut_line, // tty |
| ut->ut_host, // hostname |
| (float)ut->ut_tv.tv_sec, // tstamp |
| user_proc); // (bool) user process |
| if (tuple == NULL) |
| goto error; |
| if (PyList_Append(ret_list, tuple)) |
| goto error; |
| Py_DECREF(tuple); |
| } |
| endutent(); |
| |
| return ret_list; |
| |
| error: |
| Py_XDECREF(tuple); |
| Py_DECREF(ret_list); |
| if (ut != NULL) |
| endutent(); |
| return NULL; |
| } |
| |
| |
| /* |
| * Return disk mounted partitions as a list of tuples including device, |
| * mount point and filesystem type. |
| */ |
| static PyObject * |
| psutil_disk_partitions(PyObject *self, PyObject *args) |
| { |
| FILE *file; |
| struct mnttab mt; |
| PyObject *py_retlist = PyList_New(0); |
| PyObject *py_tuple = NULL; |
| |
| if (py_retlist == NULL) |
| return NULL; |
| |
| file = fopen(MNTTAB, "rb"); |
| if (file == NULL) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| goto error; |
| } |
| |
| while (getmntent(file, &mt) == 0) { |
| py_tuple = Py_BuildValue( |
| "(ssss)", |
| mt.mnt_special, // device |
| mt.mnt_mountp, // mount point |
| mt.mnt_fstype, // fs type |
| mt.mnt_mntopts); // options |
| if (py_tuple == NULL) |
| goto error; |
| if (PyList_Append(py_retlist, py_tuple)) |
| goto error; |
| Py_DECREF(py_tuple); |
| |
| } |
| fclose(file); |
| return py_retlist; |
| |
| error: |
| Py_XDECREF(py_tuple); |
| Py_DECREF(py_retlist); |
| if (file != NULL) |
| fclose(file); |
| return NULL; |
| } |
| |
| |
| /* |
| * Return system-wide CPU times. |
| */ |
| static PyObject * |
| psutil_per_cpu_times(PyObject *self, PyObject *args) |
| { |
| kstat_ctl_t *kc; |
| kstat_t *ksp; |
| cpu_stat_t cs; |
| PyObject *py_retlist = PyList_New(0); |
| PyObject *py_cputime = NULL; |
| |
| if (py_retlist == NULL) |
| return NULL; |
| |
| kc = kstat_open(); |
| if (kc == NULL) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| goto error; |
| } |
| |
| for (ksp = kc->kc_chain; ksp != NULL; ksp = ksp->ks_next) { |
| if (strcmp(ksp->ks_module, "cpu_stat") == 0) { |
| if (kstat_read(kc, ksp, &cs) == -1) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| goto error; |
| } |
| py_cputime = Py_BuildValue("ffff", |
| (float)cs.cpu_sysinfo.cpu[CPU_USER], |
| (float)cs.cpu_sysinfo.cpu[CPU_KERNEL], |
| (float)cs.cpu_sysinfo.cpu[CPU_IDLE], |
| (float)cs.cpu_sysinfo.cpu[CPU_WAIT]); |
| if (py_cputime == NULL) |
| goto error; |
| if (PyList_Append(py_retlist, py_cputime)) |
| goto error; |
| Py_DECREF(py_cputime); |
| py_cputime = NULL; |
| } |
| } |
| |
| kstat_close(kc); |
| return py_retlist; |
| |
| error: |
| Py_XDECREF(py_cputime); |
| Py_DECREF(py_retlist); |
| if (kc != NULL) |
| kstat_close(kc); |
| return NULL; |
| } |
| |
| |
| /* |
| * Return disk IO statistics. |
| */ |
| static PyObject * |
| psutil_disk_io_counters(PyObject *self, PyObject *args) |
| { |
| kstat_ctl_t *kc; |
| kstat_t *ksp; |
| kstat_io_t kio; |
| PyObject *py_retdict = PyDict_New(); |
| PyObject *py_disk_info = NULL; |
| |
| if (py_retdict == NULL) |
| return NULL; |
| kc = kstat_open(); |
| if (kc == NULL) { |
| PyErr_SetFromErrno(PyExc_OSError);; |
| goto error; |
| } |
| ksp = kc->kc_chain; |
| while (ksp != NULL) { |
| if (ksp->ks_type == KSTAT_TYPE_IO) { |
| if (strcmp(ksp->ks_class, "disk") == 0) { |
| if (kstat_read(kc, ksp, &kio) == -1) { |
| kstat_close(kc); |
| return PyErr_SetFromErrno(PyExc_OSError);; |
| } |
| py_disk_info = Py_BuildValue( |
| "(IIKKLL)", |
| kio.reads, |
| kio.writes, |
| kio.nread, |
| kio.nwritten, |
| kio.rtime / 1000 / 1000, // from nano to milli secs |
| kio.wtime / 1000 / 1000 // from nano to milli secs |
| ); |
| if (!py_disk_info) |
| goto error; |
| if (PyDict_SetItemString(py_retdict, ksp->ks_name, |
| py_disk_info)) |
| goto error; |
| Py_DECREF(py_disk_info); |
| } |
| } |
| ksp = ksp->ks_next; |
| } |
| kstat_close(kc); |
| |
| return py_retdict; |
| |
| error: |
| Py_XDECREF(py_disk_info); |
| Py_DECREF(py_retdict); |
| if (kc != NULL) |
| kstat_close(kc); |
| return NULL; |
| } |
| |
| |
| /* |
| * Return process memory mappings. |
| */ |
| static PyObject * |
| psutil_proc_memory_maps(PyObject *self, PyObject *args) |
| { |
| int pid; |
| int fd = -1; |
| char path[100]; |
| char perms[10]; |
| char *name; |
| struct stat st; |
| pstatus_t status; |
| |
| prxmap_t *xmap = NULL, *p; |
| off_t size; |
| size_t nread; |
| int nmap; |
| uintptr_t pr_addr_sz; |
| uintptr_t stk_base_sz, brk_base_sz; |
| |
| PyObject *pytuple = NULL; |
| PyObject *py_retlist = PyList_New(0); |
| |
| if (py_retlist == NULL) |
| return NULL; |
| if (! PyArg_ParseTuple(args, "i", &pid)) |
| goto error; |
| |
| sprintf(path, "/proc/%i/status", pid); |
| if (! psutil_file_to_struct(path, (void *)&status, sizeof(status))) |
| goto error; |
| |
| sprintf(path, "/proc/%i/xmap", pid); |
| if (stat(path, &st) == -1) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| goto error; |
| } |
| |
| size = st.st_size; |
| |
| fd = open(path, O_RDONLY); |
| if (fd == -1) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| goto error; |
| } |
| |
| xmap = (prxmap_t *)malloc(size); |
| if (xmap == NULL) { |
| PyErr_NoMemory(); |
| goto error; |
| } |
| |
| nread = pread(fd, xmap, size, 0); |
| nmap = nread / sizeof(prxmap_t); |
| p = xmap; |
| |
| while (nmap) { |
| nmap -= 1; |
| if (p == NULL) { |
| p += 1; |
| continue; |
| } |
| |
| perms[0] = '\0'; |
| pr_addr_sz = p->pr_vaddr + p->pr_size; |
| |
| // perms |
| sprintf(perms, "%c%c%c%c%c%c", p->pr_mflags & MA_READ ? 'r' : '-', |
| p->pr_mflags & MA_WRITE ? 'w' : '-', |
| p->pr_mflags & MA_EXEC ? 'x' : '-', |
| p->pr_mflags & MA_SHARED ? 's' : '-', |
| p->pr_mflags & MA_NORESERVE ? 'R' : '-', |
| p->pr_mflags & MA_RESERVED1 ? '*' : ' '); |
| |
| // name |
| if (strlen(p->pr_mapname) > 0) { |
| name = p->pr_mapname; |
| } |
| else { |
| if ((p->pr_mflags & MA_ISM) || (p->pr_mflags & MA_SHM)) { |
| name = "[shmid]"; |
| } |
| else { |
| stk_base_sz = status.pr_stkbase + status.pr_stksize; |
| brk_base_sz = status.pr_brkbase + status.pr_brksize; |
| |
| if ((pr_addr_sz > status.pr_stkbase) && |
| (p->pr_vaddr < stk_base_sz)) { |
| name = "[stack]"; |
| } |
| else if ((p->pr_mflags & MA_ANON) && \ |
| (pr_addr_sz > status.pr_brkbase) && \ |
| (p->pr_vaddr < brk_base_sz)) { |
| name = "[heap]"; |
| } |
| else { |
| name = "[anon]"; |
| } |
| } |
| } |
| |
| pytuple = Py_BuildValue("iisslll", |
| p->pr_vaddr, |
| pr_addr_sz, |
| perms, |
| name, |
| (long)p->pr_rss * p->pr_pagesize, |
| (long)p->pr_anon * p->pr_pagesize, |
| (long)p->pr_locked * p->pr_pagesize); |
| if (!pytuple) |
| goto error; |
| if (PyList_Append(py_retlist, pytuple)) |
| goto error; |
| Py_DECREF(pytuple); |
| |
| // increment pointer |
| p += 1; |
| } |
| |
| close(fd); |
| free(xmap); |
| return py_retlist; |
| |
| error: |
| if (fd != -1) |
| close(fd); |
| Py_XDECREF(pytuple); |
| Py_DECREF(py_retlist); |
| if (xmap != NULL) |
| free(xmap); |
| return NULL; |
| } |
| |
| |
| /* |
| * Return a list of tuples for network I/O statistics. |
| */ |
| static PyObject * |
| psutil_net_io_counters(PyObject *self, PyObject *args) |
| { |
| kstat_ctl_t *kc = NULL; |
| kstat_t *ksp; |
| kstat_named_t *rbytes, *wbytes, *rpkts, *wpkts, *ierrs, *oerrs; |
| |
| PyObject *py_retdict = PyDict_New(); |
| PyObject *py_ifc_info = NULL; |
| |
| if (py_retdict == NULL) |
| return NULL; |
| kc = kstat_open(); |
| if (kc == NULL) |
| goto error; |
| |
| ksp = kc->kc_chain; |
| while (ksp != NULL) { |
| if (ksp->ks_type != KSTAT_TYPE_NAMED) |
| goto next; |
| if (strcmp(ksp->ks_class, "net") != 0) |
| goto next; |
| /* |
| // XXX "lo" (localhost) interface makes kstat_data_lookup() fail |
| // (maybe because "ifconfig -a" says it's a virtual interface?). |
| if ((strcmp(ksp->ks_module, "link") != 0) && |
| (strcmp(ksp->ks_module, "lo") != 0)) { |
| goto skip; |
| */ |
| if ((strcmp(ksp->ks_module, "link") != 0)) |
| goto next; |
| |
| if (kstat_read(kc, ksp, NULL) == -1) { |
| errno = 0; |
| continue; |
| } |
| |
| rbytes = (kstat_named_t *)kstat_data_lookup(ksp, "rbytes"); |
| wbytes = (kstat_named_t *)kstat_data_lookup(ksp, "obytes"); |
| rpkts = (kstat_named_t *)kstat_data_lookup(ksp, "ipackets"); |
| wpkts = (kstat_named_t *)kstat_data_lookup(ksp, "opackets"); |
| ierrs = (kstat_named_t *)kstat_data_lookup(ksp, "ierrors"); |
| oerrs = (kstat_named_t *)kstat_data_lookup(ksp, "oerrors"); |
| |
| if ((rbytes == NULL) || (wbytes == NULL) || (rpkts == NULL) || |
| (wpkts == NULL) || (ierrs == NULL) || (oerrs == NULL)) |
| { |
| PyErr_SetString(PyExc_RuntimeError, "kstat_data_lookup() failed"); |
| goto error; |
| } |
| |
| #if defined(_INT64_TYPE) |
| py_ifc_info = Py_BuildValue("(KKKKkkii)", |
| wbytes->value.ui64, |
| rbytes->value.ui64, |
| wpkts->value.ui64, |
| rpkts->value.ui64, |
| ierrs->value.ui32, |
| oerrs->value.ui32, |
| #else |
| py_ifc_info = Py_BuildValue("(kkkkkkii)", |
| wbytes->value.ui32, |
| rbytes->value.ui32, |
| wpkts->value.ui32, |
| rpkts->value.ui32, |
| ierrs->value.ui32, |
| oerrs->value.ui32, |
| #endif |
| 0, // dropin not supported |
| 0 // dropout not supported |
| ); |
| if (!py_ifc_info) |
| goto error; |
| if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info)) |
| goto error; |
| Py_DECREF(py_ifc_info); |
| goto next; |
| |
| next: |
| ksp = ksp->ks_next; |
| } |
| |
| kstat_close(kc); |
| return py_retdict; |
| |
| error: |
| Py_XDECREF(py_ifc_info); |
| Py_DECREF(py_retdict); |
| if (kc != NULL) |
| kstat_close(kc); |
| return NULL; |
| } |
| |
| |
| #ifndef EXPER_IP_AND_ALL_IRES |
| #define EXPER_IP_AND_ALL_IRES (1024+4) |
| #endif |
| |
| // a signaler for connections without an actual status |
| static int PSUTIL_CONN_NONE = 128; |
| |
| /* |
| * Return TCP and UDP connections opened by process. |
| * UNIX sockets are excluded. |
| * |
| * Thanks to: |
| * https://github.com/DavidGriffith/finx/blob/master/ |
| * nxsensor-3.5.0-1/src/sysdeps/solaris.c |
| * ...and: |
| * https://hg.java.net/hg/solaris~on-src/file/tip/usr/src/cmd/ |
| * cmd-inet/usr.bin/netstat/netstat.c |
| */ |
| static PyObject * |
| psutil_net_connections(PyObject *self, PyObject *args) |
| { |
| long pid; |
| int sd = 0; |
| mib2_tcpConnEntry_t *tp = NULL; |
| mib2_udpEntry_t *ude; |
| #if defined(AF_INET6) |
| mib2_tcp6ConnEntry_t *tp6; |
| mib2_udp6Entry_t *ude6; |
| #endif |
| char buf[512]; |
| int i, flags, getcode, num_ent, state; |
| char lip[200], rip[200]; |
| int lport, rport; |
| int processed_pid; |
| int databuf_init = 0; |
| struct strbuf ctlbuf, databuf; |
| struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf; |
| struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf; |
| struct T_error_ack *tea = (struct T_error_ack *)buf; |
| struct opthdr *mibhdr; |
| |
| PyObject *py_retlist = PyList_New(0); |
| PyObject *py_tuple = NULL; |
| PyObject *py_laddr = NULL; |
| PyObject *py_raddr = NULL; |
| PyObject *af_filter = NULL; |
| PyObject *type_filter = NULL; |
| |
| if (py_retlist == NULL) |
| return NULL; |
| if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) |
| goto error; |
| if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) { |
| PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); |
| goto error; |
| } |
| |
| sd = open("/dev/arp", O_RDWR); |
| if (sd == -1) { |
| PyErr_SetFromErrnoWithFilename(PyExc_OSError, "/dev/arp"); |
| goto error; |
| } |
| |
| /* |
| XXX - These 2 are used in ifconfig.c but they seem unnecessary |
| ret = ioctl(sd, I_PUSH, "tcp"); |
| if (ret == -1) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| goto error; |
| } |
| ret = ioctl(sd, I_PUSH, "udp"); |
| if (ret == -1) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| goto error; |
| } |
| */ |
| |
| // OK, this mess is basically copied and pasted from nxsensor project |
| // which copied and pasted it from netstat source code, mibget() |
| // function. Also see: |
| // http://stackoverflow.com/questions/8723598/ |
| tor->PRIM_type = T_SVR4_OPTMGMT_REQ; |
| tor->OPT_offset = sizeof (struct T_optmgmt_req); |
| tor->OPT_length = sizeof (struct opthdr); |
| tor->MGMT_flags = T_CURRENT; |
| mibhdr = (struct opthdr *)&tor[1]; |
| mibhdr->level = EXPER_IP_AND_ALL_IRES; |
| mibhdr->name = 0; |
| mibhdr->len = 0; |
| |
| ctlbuf.buf = buf; |
| ctlbuf.len = tor->OPT_offset + tor->OPT_length; |
| flags = 0; // request to be sent in non-priority |
| |
| if (putmsg(sd, &ctlbuf, (struct strbuf *)0, flags) == -1) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| goto error; |
| } |
| |
| mibhdr = (struct opthdr *)&toa[1]; |
| ctlbuf.maxlen = sizeof (buf); |
| |
| for (;;) { |
| flags = 0; |
| getcode = getmsg(sd, &ctlbuf, (struct strbuf *)0, &flags); |
| |
| if (getcode != MOREDATA || |
| ctlbuf.len < sizeof (struct T_optmgmt_ack) || |
| toa->PRIM_type != T_OPTMGMT_ACK || |
| toa->MGMT_flags != T_SUCCESS) |
| { |
| break; |
| } |
| if (ctlbuf.len >= sizeof (struct T_error_ack) && |
| tea->PRIM_type == T_ERROR_ACK) |
| { |
| PyErr_SetString(PyExc_RuntimeError, "ERROR_ACK"); |
| goto error; |
| } |
| if (getcode == 0 && |
| ctlbuf.len >= sizeof (struct T_optmgmt_ack) && |
| toa->PRIM_type == T_OPTMGMT_ACK && |
| toa->MGMT_flags == T_SUCCESS) |
| { |
| PyErr_SetString(PyExc_RuntimeError, "ERROR_T_OPTMGMT_ACK"); |
| goto error; |
| } |
| |
| databuf.maxlen = mibhdr->len; |
| databuf.len = 0; |
| databuf.buf = (char *)malloc((int)mibhdr->len); |
| if (!databuf.buf) { |
| PyErr_NoMemory(); |
| goto error; |
| } |
| databuf_init = 1; |
| |
| flags = 0; |
| getcode = getmsg(sd, (struct strbuf *)0, &databuf, &flags); |
| if (getcode < 0) { |
| PyErr_SetFromErrno(PyExc_OSError); |
| goto error; |
| } |
| |
| // TCPv4 |
| if (mibhdr->level == MIB2_TCP && mibhdr->name == MIB2_TCP_13) { |
| tp = (mib2_tcpConnEntry_t *)databuf.buf; |
| num_ent = mibhdr->len / sizeof(mib2_tcpConnEntry_t); |
| for (i = 0; i < num_ent; i++, tp++) { |
| processed_pid = tp->tcpConnCreationProcess; |
| if (pid != -1 && processed_pid != pid) |
| continue; |
| // construct local/remote addresses |
| inet_ntop(AF_INET, &tp->tcpConnLocalAddress, lip, sizeof(lip)); |
| inet_ntop(AF_INET, &tp->tcpConnRemAddress, rip, sizeof(rip)); |
| lport = tp->tcpConnLocalPort; |
| rport = tp->tcpConnRemPort; |
| |
| // contruct python tuple/list |
| py_laddr = Py_BuildValue("(si)", lip, lport); |
| if (!py_laddr) |
| goto error; |
| if (rport != 0) |
| py_raddr = Py_BuildValue("(si)", rip, rport); |
| else { |
| py_raddr = Py_BuildValue("()"); |
| } |
| if (!py_raddr) |
| goto error; |
| state = tp->tcpConnEntryInfo.ce_state; |
| |
| // add item |
| py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET, SOCK_STREAM, |
| py_laddr, py_raddr, state, |
| processed_pid); |
| if (!py_tuple) |
| goto error; |
| if (PyList_Append(py_retlist, py_tuple)) |
| goto error; |
| Py_DECREF(py_tuple); |
| } |
| } |
| #if defined(AF_INET6) |
| // TCPv6 |
| else if (mibhdr->level == MIB2_TCP6 && mibhdr->name == MIB2_TCP6_CONN) |
| { |
| tp6 = (mib2_tcp6ConnEntry_t *)databuf.buf; |
| num_ent = mibhdr->len / sizeof(mib2_tcp6ConnEntry_t); |
| |
| for (i = 0; i < num_ent; i++, tp6++) { |
| processed_pid = tp6->tcp6ConnCreationProcess; |
| if (pid != -1 && processed_pid != pid) |
| continue; |
| // construct local/remote addresses |
| inet_ntop(AF_INET6, &tp6->tcp6ConnLocalAddress, lip, sizeof(lip)); |
| inet_ntop(AF_INET6, &tp6->tcp6ConnRemAddress, rip, sizeof(rip)); |
| lport = tp6->tcp6ConnLocalPort; |
| rport = tp6->tcp6ConnRemPort; |
| |
| // contruct python tuple/list |
| py_laddr = Py_BuildValue("(si)", lip, lport); |
| if (!py_laddr) |
| goto error; |
| if (rport != 0) |
| py_raddr = Py_BuildValue("(si)", rip, rport); |
| else |
| py_raddr = Py_BuildValue("()"); |
| if (!py_raddr) |
| goto error; |
| state = tp6->tcp6ConnEntryInfo.ce_state; |
| |
| // add item |
| py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET6, SOCK_STREAM, |
| py_laddr, py_raddr, state, processed_pid); |
| if (!py_tuple) |
| goto error; |
| if (PyList_Append(py_retlist, py_tuple)) |
| goto error; |
| Py_DECREF(py_tuple); |
| } |
| } |
| #endif |
| // UDPv4 |
| else if (mibhdr->level == MIB2_UDP || mibhdr->level == MIB2_UDP_ENTRY) { |
| ude = (mib2_udpEntry_t *)databuf.buf; |
| num_ent = mibhdr->len / sizeof(mib2_udpEntry_t); |
| for (i = 0; i < num_ent; i++, ude++) { |
| processed_pid = ude->udpCreationProcess; |
| if (pid != -1 && processed_pid != pid) |
| continue; |
| // XXX Very ugly hack! It seems we get here only the first |
| // time we bump into a UDPv4 socket. PID is a very high |
| // number (clearly impossible) and the address does not |
| // belong to any valid interface. Not sure what else |
| // to do other than skipping. |
| if (processed_pid > 131072) |
| continue; |
| inet_ntop(AF_INET, &ude->udpLocalAddress, lip, sizeof(lip)); |
| lport = ude->udpLocalPort; |
| py_laddr = Py_BuildValue("(si)", lip, lport); |
| if (!py_laddr) |
| goto error; |
| py_raddr = Py_BuildValue("()"); |
| if (!py_raddr) |
| goto error; |
| py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET, SOCK_DGRAM, |
| py_laddr, py_raddr, PSUTIL_CONN_NONE, |
| processed_pid); |
| if (!py_tuple) |
| goto error; |
| if (PyList_Append(py_retlist, py_tuple)) |
| goto error; |
| Py_DECREF(py_tuple); |
| } |
| } |
| #if defined(AF_INET6) |
| // UDPv6 |
| else if (mibhdr->level == MIB2_UDP6 || |
| mibhdr->level == MIB2_UDP6_ENTRY) |
| { |
| ude6 = (mib2_udp6Entry_t *)databuf.buf; |
| num_ent = mibhdr->len / sizeof(mib2_udp6Entry_t); |
| for (i = 0; i < num_ent; i++, ude6++) { |
| processed_pid = ude6->udp6CreationProcess; |
| if (pid != -1 && processed_pid != pid) |
| continue; |
| inet_ntop(AF_INET6, &ude6->udp6LocalAddress, lip, sizeof(lip)); |
| lport = ude6->udp6LocalPort; |
| py_laddr = Py_BuildValue("(si)", lip, lport); |
| if (!py_laddr) |
| goto error; |
| py_raddr = Py_BuildValue("()"); |
| if (!py_raddr) |
| goto error; |
| py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET6, SOCK_DGRAM, |
| py_laddr, py_raddr, PSUTIL_CONN_NONE, |
| processed_pid); |
| if (!py_tuple) |
| goto error; |
| if (PyList_Append(py_retlist, py_tuple)) |
| goto error; |
| Py_DECREF(py_tuple); |
| } |
| } |
| #endif |
| free(databuf.buf); |
| } |
| |
| close(sd); |
| return py_retlist; |
| |
| error: |
| Py_XDECREF(py_tuple); |
| Py_XDECREF(py_laddr); |
| Py_XDECREF(py_raddr); |
| Py_DECREF(py_retlist); |
| if (databuf_init == 1) |
| free(databuf.buf); |
| if (sd != 0) |
| close(sd); |
| return NULL; |
| } |
| |
| |
| static PyObject * |
| psutil_boot_time(PyObject *self, PyObject *args) |
| { |
| float boot_time = 0.0; |
| struct utmpx *ut; |
| |
| while (NULL != (ut = getutxent())) { |
| if (ut->ut_type == BOOT_TIME) { |
| boot_time = (float)ut->ut_tv.tv_sec; |
| break; |
| } |
| } |
| endutent(); |
| if (boot_time != 0.0) { |
| return Py_BuildValue("f", boot_time); |
| } |
| else { |
| PyErr_SetString(PyExc_RuntimeError, "can't determine boot time"); |
| return NULL; |
| } |
| } |
| |
| |
| /* |
| * Return the number of physical CPU cores on the system. |
| */ |
| static PyObject * |
| psutil_cpu_count_phys(PyObject *self, PyObject *args) |
| { |
| kstat_ctl_t *kc; |
| kstat_t *ksp; |
| int ncpus = 0; |
| |
| kc = kstat_open(); |
| if (kc == NULL) |
| goto error; |
| ksp = kstat_lookup(kc, "cpu_info", -1, NULL); |
| if (ksp == NULL) |
| goto error; |
| |
| for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { |
| if (strcmp(ksp->ks_module, "cpu_info") != 0) |
| continue; |
| if (kstat_read(kc, ksp, NULL) == -1) |
| goto error; |
| ncpus += 1; |
| } |
| |
| kstat_close(kc); |
| if (ncpus > 0) |
| return Py_BuildValue("i", ncpus); |
| else |
| goto error; |
| |
| error: |
| // mimic os.cpu_count() |
| if (kc != NULL) |
| kstat_close(kc); |
| Py_RETURN_NONE; |
| } |
| |
| |
| /* |
| * Return stats about a particular network |
| * interface. References: |
| * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py |
| * http://www.i-scream.org/libstatgrab/ |
| */ |
| static PyObject* |
| psutil_net_if_stats(PyObject* self, PyObject* args) |
| { |
| kstat_ctl_t *kc = NULL; |
| kstat_t *ksp; |
| kstat_named_t *knp; |
| int ret; |
| int sock = 0; |
| int duplex; |
| int speed; |
| |
| PyObject *py_retdict = PyDict_New(); |
| PyObject *py_ifc_info = NULL; |
| PyObject *py_is_up = NULL; |
| |
| if (py_retdict == NULL) |
| return NULL; |
| kc = kstat_open(); |
| if (kc == NULL) |
| goto error; |
| sock = socket(AF_INET, SOCK_DGRAM, 0); |
| if (sock == -1) |
| goto error; |
| |
| for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { |
| if (strcmp(ksp->ks_class, "net") == 0) { |
| struct ifreq ifr; |
| |
| kstat_read(kc, ksp, NULL); |
| if (ksp->ks_type != KSTAT_TYPE_NAMED) |
| continue; |
| if (strcmp(ksp->ks_class, "net") != 0) |
| continue; |
| |
| strncpy(ifr.ifr_name, ksp->ks_name, sizeof(ifr.ifr_name)); |
| ret = ioctl(sock, SIOCGIFFLAGS, &ifr); |
| if (ret == -1) |
| continue; // not a network interface |
| |
| // is up? |
| if ((ifr.ifr_flags & IFF_UP) != 0) { |
| if ((knp = kstat_data_lookup(ksp, "link_up")) != NULL) { |
| if (knp->value.ui32 != 0u) |
| py_is_up = Py_True; |
| else |
| py_is_up = Py_False; |
| } |
| else { |
| py_is_up = Py_True; |
| } |
| } |
| else { |
| py_is_up = Py_False; |
| } |
| Py_INCREF(py_is_up); |
| |
| // duplex |
| duplex = 0; // unknown |
| if ((knp = kstat_data_lookup(ksp, "link_duplex")) != NULL) { |
| if (knp->value.ui32 == 1) |
| duplex = 1; // half |
| else if (knp->value.ui32 == 2) |
| duplex = 2; // full |
| } |
| |
| // speed |
| if ((knp = kstat_data_lookup(ksp, "ifspeed")) != NULL) |
| // expressed in bits per sec, we want mega bits per sec |
| speed = (int)knp->value.ui64 / 1000000; |
| else |
| speed = 0; |
| |
| // mtu |
| ret = ioctl(sock, SIOCGIFMTU, &ifr); |
| if (ret == -1) |
| goto error; |
| |
| py_ifc_info = Py_BuildValue("(Oiii)", py_is_up, duplex, speed, |
| ifr.ifr_mtu); |
| if (!py_ifc_info) |
| goto error; |
| if (PyDict_SetItemString(py_retdict, ksp->ks_name, py_ifc_info)) |
| goto error; |
| Py_DECREF(py_ifc_info); |
| } |
| } |
| |
| close(sock); |
| kstat_close(kc); |
| return py_retdict; |
| |
| error: |
| Py_XDECREF(py_is_up); |
| Py_XDECREF(py_ifc_info); |
| Py_DECREF(py_retdict); |
| if (sock != 0) |
| close(sock); |
| if (kc != NULL) |
| kstat_close(kc); |
| PyErr_SetFromErrno(PyExc_OSError); |
| return NULL; |
| } |
| |
| |
| /* |
| * define the psutil C module methods and initialize the module. |
| */ |
| static PyMethodDef |
| PsutilMethods[] = |
| { |
| // --- process-related functions |
| {"proc_basic_info", psutil_proc_basic_info, METH_VARARGS, |
| "Return process ppid, rss, vms, ctime, nice, nthreads, status and tty"}, |
| {"proc_name_and_args", psutil_proc_name_and_args, METH_VARARGS, |
| "Return process name and args."}, |
| {"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS, |
| "Return process user and system CPU times."}, |
| {"proc_cred", psutil_proc_cred, METH_VARARGS, |
| "Return process uids/gids."}, |
| {"query_process_thread", psutil_proc_query_thread, METH_VARARGS, |
| "Return info about a process thread"}, |
| {"proc_memory_maps", psutil_proc_memory_maps, METH_VARARGS, |
| "Return process memory mappings"}, |
| {"proc_num_ctx_switches", psutil_proc_num_ctx_switches, METH_VARARGS, |
| "Return the number of context switches performed by process"}, |
| |
| // --- system-related functions |
| {"swap_mem", psutil_swap_mem, METH_VARARGS, |
| "Return information about system swap memory."}, |
| {"users", psutil_users, METH_VARARGS, |
| "Return currently connected users."}, |
| {"disk_partitions", psutil_disk_partitions, METH_VARARGS, |
| "Return disk partitions."}, |
| {"per_cpu_times", psutil_per_cpu_times, METH_VARARGS, |
| "Return system per-CPU times."}, |
| {"disk_io_counters", psutil_disk_io_counters, METH_VARARGS, |
| "Return a Python dict of tuples for disk I/O statistics."}, |
| {"net_io_counters", psutil_net_io_counters, METH_VARARGS, |
| "Return a Python dict of tuples for network I/O statistics."}, |
| {"boot_time", psutil_boot_time, METH_VARARGS, |
| "Return system boot time in seconds since the EPOCH."}, |
| {"cpu_count_phys", psutil_cpu_count_phys, METH_VARARGS, |
| "Return the number of physical CPUs on the system."}, |
| {"net_connections", psutil_net_connections, METH_VARARGS, |
| "Return TCP and UDP syste-wide open connections."}, |
| {"net_if_stats", psutil_net_if_stats, METH_VARARGS, |
| "Return NIC stats (isup, duplex, speed, mtu)"}, |
| |
| {NULL, NULL, 0, NULL} |
| }; |
| |
| |
| struct module_state { |
| PyObject *error; |
| }; |
| |
| #if PY_MAJOR_VERSION >= 3 |
| #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) |
| #else |
| #define GETSTATE(m) (&_state) |
| #endif |
| |
| #if PY_MAJOR_VERSION >= 3 |
| |
| static int |
| psutil_sunos_traverse(PyObject *m, visitproc visit, void *arg) { |
| Py_VISIT(GETSTATE(m)->error); |
| return 0; |
| } |
| |
| static int |
| psutil_sunos_clear(PyObject *m) { |
| Py_CLEAR(GETSTATE(m)->error); |
| return 0; |
| } |
| |
| static struct PyModuleDef moduledef = { |
| PyModuleDef_HEAD_INIT, |
| "psutil_sunos", |
| NULL, |
| sizeof(struct module_state), |
| PsutilMethods, |
| NULL, |
| psutil_sunos_traverse, |
| psutil_sunos_clear, |
| NULL |
| }; |
| |
| #define INITERROR return NULL |
| |
| PyMODINIT_FUNC PyInit__psutil_sunos(void) |
| |
| #else |
| #define INITERROR return |
| |
| void init_psutil_sunos(void) |
| #endif |
| { |
| #if PY_MAJOR_VERSION >= 3 |
| PyObject *module = PyModule_Create(&moduledef); |
| #else |
| PyObject *module = Py_InitModule("_psutil_sunos", PsutilMethods); |
| #endif |
| PyModule_AddIntConstant(module, "version", PSUTIL_VERSION); |
| |
| PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); |
| PyModule_AddIntConstant(module, "SRUN", SRUN); |
| PyModule_AddIntConstant(module, "SZOMB", SZOMB); |
| PyModule_AddIntConstant(module, "SSTOP", SSTOP); |
| PyModule_AddIntConstant(module, "SIDL", SIDL); |
| PyModule_AddIntConstant(module, "SONPROC", SONPROC); |
| PyModule_AddIntConstant(module, "SWAIT", SWAIT); |
| |
| PyModule_AddIntConstant(module, "PRNODEV", PRNODEV); // for process tty |
| |
| PyModule_AddIntConstant(module, "TCPS_CLOSED", TCPS_CLOSED); |
| PyModule_AddIntConstant(module, "TCPS_CLOSING", TCPS_CLOSING); |
| PyModule_AddIntConstant(module, "TCPS_CLOSE_WAIT", TCPS_CLOSE_WAIT); |
| PyModule_AddIntConstant(module, "TCPS_LISTEN", TCPS_LISTEN); |
| PyModule_AddIntConstant(module, "TCPS_ESTABLISHED", TCPS_ESTABLISHED); |
| PyModule_AddIntConstant(module, "TCPS_SYN_SENT", TCPS_SYN_SENT); |
| PyModule_AddIntConstant(module, "TCPS_SYN_RCVD", TCPS_SYN_RCVD); |
| PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_1", TCPS_FIN_WAIT_1); |
| PyModule_AddIntConstant(module, "TCPS_FIN_WAIT_2", TCPS_FIN_WAIT_2); |
| PyModule_AddIntConstant(module, "TCPS_LAST_ACK", TCPS_LAST_ACK); |
| PyModule_AddIntConstant(module, "TCPS_TIME_WAIT", TCPS_TIME_WAIT); |
| // sunos specific |
| PyModule_AddIntConstant(module, "TCPS_IDLE", TCPS_IDLE); |
| // sunos specific |
| PyModule_AddIntConstant(module, "TCPS_BOUND", TCPS_BOUND); |
| PyModule_AddIntConstant(module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE); |
| |
| if (module == NULL) |
| INITERROR; |
| #if PY_MAJOR_VERSION >= 3 |
| return module; |
| #endif |
| } |