/* * proc_cache_find_by_pid() * find process info by the process id, if it is not found * and it is a traceable process then cache it */ proc_info_t *proc_cache_find_by_pid(const pid_t pid) { unsigned long h; proc_info_t *p; pthread_mutex_lock(&proc_cache_mutex); h = proc_cache_hash_pid(pid); for (p = proc_cache_hash[h]; p; p = p->next) { if (p->pid == pid) { pthread_mutex_unlock(&proc_cache_mutex); return p; } } pthread_mutex_unlock(&proc_cache_mutex); /* * Not found, so add it and return it if it is a legitimate * process to trace */ if (!pid_exists(pid) || (pid == getpid())) return NULL; /* Be lazy and ignore the parent info lookup */ return proc_cache_add_at_hash_index(h, pid, 0, false); }
/* * Return process pathname executable. * Thanks to Robert N. M. Watson: * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT */ static PyObject* get_process_exe(PyObject* self, PyObject* args) { long pid; char pathname[PATH_MAX]; int error; int mib[4]; size_t size; if (! PyArg_ParseTuple(args, "l", &pid)) { return NULL; } mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = pid; size = sizeof(pathname); error = sysctl(mib, 4, pathname, &size, NULL, 0); if (error == -1) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } if (size == 0 || strlen(pathname) == 0) { if (pid_exists(pid) == 0) { return NoSuchProcess(); } else { strcpy(pathname, ""); } } return Py_BuildValue("s", pathname); }
/* * Return number of threads used by process as a Python integer. */ static PyObject* get_process_num_threads(PyObject* self, PyObject* args) { long pid; int err, ret; unsigned int info_count = TASK_BASIC_INFO_COUNT; mach_port_t task; struct task_basic_info tasks_info; thread_act_port_array_t thread_list; mach_msg_type_number_t thread_count; // the argument passed should be a process id if (! PyArg_ParseTuple(args, "l", &pid)) { return NULL; } /* task_for_pid() requires special privileges * "This function can be called only if the process is owned by the * procmod group or if the caller is root." * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html */ err = task_for_pid(mach_task_self(), pid, &task); if ( err == KERN_SUCCESS) { info_count = TASK_BASIC_INFO_COUNT; err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); if (err != KERN_SUCCESS) { // errcode 4 is "invalid argument" (access denied) if (err == 4) { return AccessDenied(); } // otherwise throw a runtime error with appropriate error code return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_INFO) failed"); } err = task_threads(task, &thread_list, &thread_count); if (err == KERN_SUCCESS) { ret = vm_deallocate(task, (vm_address_t)thread_list, thread_count * sizeof(int)); if (ret != KERN_SUCCESS) { printf("vm_deallocate() failed\n"); } return Py_BuildValue("l", (long)thread_count); } else { return PyErr_Format(PyExc_RuntimeError, "task_thread() failed"); } } else { if (! pid_exists(pid) ) { return NoSuchProcess(); } // pid exists, so return AccessDenied error since task_for_pid() failed return AccessDenied(); } return NULL; }
/* * proc_cache_add() * explicity add process info to global cache ONLY if it is a traceable process */ proc_info_t *proc_cache_add(const pid_t pid, const pid_t ppid, const bool is_thread) { proc_info_t *p; unsigned long h; if (!pid_exists(pid) || (pid == getpid())) return NULL; pthread_mutex_lock(&proc_cache_mutex); h = proc_cache_hash_pid(pid); for (p = proc_cache_hash[h]; p; p = p->next) { if (p->pid == pid) { pthread_mutex_unlock(&proc_cache_mutex); return p; } } pthread_mutex_unlock(&proc_cache_mutex); return proc_cache_add_at_hash_index(h, pid, ppid, is_thread); }
/* * Return process TCP and UDP connections as a list of tuples. * References: * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/wNrC0 * - /usr/include/sys/proc_info.h */ static PyObject* get_process_connections(PyObject* self, PyObject* args) { long pid; int pidinfo_result; int iterations; int i; int nb; struct proc_fdinfo *fds_pointer; struct proc_fdinfo *fdp_pointer; struct socket_fdinfo si; PyObject *retList = PyList_New(0); PyObject *tuple = NULL; PyObject *laddr = NULL; PyObject *raddr = NULL; PyObject *af_filter = NULL; PyObject *type_filter = NULL; if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) { return NULL; } if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) { PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); return NULL; } if (pid == 0) { return retList; } pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); if (pidinfo_result <= 0) { goto error; } fds_pointer = malloc(pidinfo_result); pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result); free(fds_pointer); if (pidinfo_result <= 0) { goto error; } iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE); for (i = 0; i < iterations; i++) { errno = 0; fdp_pointer = &fds_pointer[i]; // if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET) { nb = proc_pidfdinfo(pid, fdp_pointer->proc_fd, PROC_PIDFDSOCKETINFO, &si, sizeof(si)); // --- errors checking if (nb <= 0) { if (errno == EBADF) { // let's assume socket has been closed continue; } if (errno != 0) { return PyErr_SetFromErrno(PyExc_OSError); } else { return PyErr_Format(PyExc_RuntimeError, "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed"); } } if (nb < sizeof(si)) { return PyErr_Format(PyExc_RuntimeError, "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed (buffer mismatch)"); } // --- /errors checking // int fd, family, type, lport, rport; char lip[200], rip[200]; char *state; int inseq; PyObject* _family; PyObject* _type; fd = (int)fdp_pointer->proc_fd; family = si.psi.soi_family; type = si.psi.soi_kind; if (type == 2) { type = SOCK_STREAM; } else if (type == 1) { type = SOCK_DGRAM; } else { continue; } // apply filters _family = PyLong_FromLong((long)family); inseq = PySequence_Contains(af_filter, _family); Py_DECREF(_family); if (inseq == 0) { continue; } _type = PyLong_FromLong((long)type); inseq = PySequence_Contains(type_filter, _type); Py_DECREF(_type); if (inseq == 0) { continue; } if (errno != 0) { return PyErr_SetFromErrno(PyExc_OSError); } if (family == AF_INET) { inet_ntop(AF_INET, &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_46.i46a_addr4, lip, sizeof(lip)); inet_ntop(AF_INET, &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_46.i46a_addr4, rip, sizeof(lip)); } else { inet_ntop(AF_INET6, &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_6, lip, sizeof(lip)); inet_ntop(AF_INET6, &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_6, lip, sizeof(rip)); } // check for inet_ntop failures if (errno != 0) { return PyErr_SetFromErrno(PyExc_OSError); } lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport); rport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport); if (type == SOCK_STREAM) { state = get_connection_status((int)si.psi.soi_proto.pri_tcp.tcpsi_state); } else { state = ""; } laddr = Py_BuildValue("(si)", lip, lport); if (rport != 0) { raddr = Py_BuildValue("(si)", rip, rport); } else { raddr = PyTuple_New(0); } // --- construct python list tuple = Py_BuildValue("(iiiNNs)", fd, family, type, laddr, raddr, state); PyList_Append(retList, tuple); Py_DECREF(tuple); // --- /construct python list } } return retList; error: if (errno != 0) { return PyErr_SetFromErrno(PyExc_OSError); } else if (! pid_exists(pid) ) { return NoSuchProcess(); } else { return PyErr_Format(PyExc_RuntimeError, "proc_pidinfo(PROC_PIDLISTFDS) failed"); } }
/* * Return process open files as a Python tuple. * References: * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/m78fd * - /usr/include/sys/proc_info.h */ static PyObject* get_process_open_files(PyObject* self, PyObject* args) { long pid; int pidinfo_result; int iterations; int i; int nb; struct proc_fdinfo *fds_pointer; struct proc_fdinfo *fdp_pointer; struct vnode_fdinfowithpath vi; PyObject *retList = PyList_New(0); PyObject *tuple = NULL; if (! PyArg_ParseTuple(args, "l", &pid)) { return NULL; } pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); if (pidinfo_result <= 0) { goto error; } fds_pointer = malloc(pidinfo_result); pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result); free(fds_pointer); if (pidinfo_result <= 0) { goto error; } iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE); for (i = 0; i < iterations; i++) { fdp_pointer = &fds_pointer[i]; // if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) { nb = proc_pidfdinfo(pid, fdp_pointer->proc_fd, PROC_PIDFDVNODEPATHINFO, &vi, sizeof(vi)); // --- errors checking if (nb <= 0) { if ((errno == ENOENT) || (errno == EBADF)) { // no such file or directory or bad file descriptor; // let's assume the file has been closed or removed continue; } if (errno != 0) { return PyErr_SetFromErrno(PyExc_OSError); } else return PyErr_Format(PyExc_RuntimeError, "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed"); } if (nb < sizeof(vi)) { return PyErr_Format(PyExc_RuntimeError, "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed (buffer mismatch)"); } // --- /errors checking // --- construct python list tuple = Py_BuildValue("(si)", vi.pvip.vip_path, (int)fdp_pointer->proc_fd); PyList_Append(retList, tuple); Py_DECREF(tuple); // --- /construct python list } } return retList; error: if (errno != 0) { return PyErr_SetFromErrno(PyExc_OSError); } else if (! pid_exists(pid) ) { return NoSuchProcess(); } else { return PyErr_Format(PyExc_RuntimeError, "proc_pidinfo(PROC_PIDLISTFDS) failed"); } }
/* * Return process threads */ static PyObject* get_process_threads(PyObject* self, PyObject* args) { long pid; int err, j, ret; kern_return_t kr; unsigned int info_count = TASK_BASIC_INFO_COUNT; mach_port_t task; struct task_basic_info tasks_info; thread_act_port_array_t thread_list; thread_info_data_t thinfo; thread_basic_info_t basic_info_th; mach_msg_type_number_t thread_count, thread_info_count; PyObject* retList = PyList_New(0); PyObject* pyTuple = NULL; // the argument passed should be a process id if (! PyArg_ParseTuple(args, "l", &pid)) { return NULL; } // task_for_pid() requires special privileges err = task_for_pid(mach_task_self(), pid, &task); if (err != KERN_SUCCESS) { if (! pid_exists(pid) ) { return NoSuchProcess(); } return AccessDenied(); } info_count = TASK_BASIC_INFO_COUNT; err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); if (err != KERN_SUCCESS) { // errcode 4 is "invalid argument" (access denied) if (err == 4) { return AccessDenied(); } // otherwise throw a runtime error with appropriate error code return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_INFO) failed"); } err = task_threads(task, &thread_list, &thread_count); if (err != KERN_SUCCESS) { return PyErr_Format(PyExc_RuntimeError, "task_threads() failed"); } for (j = 0; j < thread_count; j++) { thread_info_count = THREAD_INFO_MAX; kr = thread_info(thread_list[j], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count); if (kr != KERN_SUCCESS) { return PyErr_Format(PyExc_RuntimeError, "thread_info() failed"); } basic_info_th = (thread_basic_info_t)thinfo; // XXX - thread_info structure does not provide any process id; // the best we can do is assigning an incremental bogus value pyTuple = Py_BuildValue("Iff", j + 1, (float)basic_info_th->user_time.microseconds / 1000000.0, (float)basic_info_th->system_time.microseconds / 1000000.0 ); PyList_Append(retList, pyTuple); Py_XDECREF(pyTuple); } ret = vm_deallocate(task, (vm_address_t)thread_list, thread_count * sizeof(int)); if (ret != KERN_SUCCESS) { printf("vm_deallocate() failed\n"); } return retList; }
/* * Return a tuple of RSS and VMS memory usage. */ static PyObject* get_memory_info(PyObject* self, PyObject* args) { long pid; int err; unsigned int info_count = TASK_BASIC_INFO_COUNT; mach_port_t task; struct task_basic_info tasks_info; vm_region_basic_info_data_64_t b_info; vm_address_t address = GLOBAL_SHARED_TEXT_SEGMENT; vm_size_t size; mach_port_t object_name; // the argument passed should be a process id if (! PyArg_ParseTuple(args, "l", &pid)) { return NULL; } /* task_for_pid() requires special privileges * "This function can be called only if the process is owned by the * procmod group or if the caller is root." * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html */ err = task_for_pid(mach_task_self(), pid, &task); if ( err == KERN_SUCCESS) { info_count = TASK_BASIC_INFO_COUNT; err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); if (err != KERN_SUCCESS) { if (err == 4) { // errcode 4 is "invalid argument" (access denied) return AccessDenied(); } // otherwise throw a runtime error with appropriate error code return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_INFO) failed"); } /* Issue #73 http://code.google.com/p/psutil/issues/detail?id=73 * adjust the virtual memory size down to account for * shared memory that task_info.virtual_size includes w/every process */ info_count = VM_REGION_BASIC_INFO_COUNT_64; err = vm_region_64(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&b_info, &info_count, &object_name); if (err == KERN_SUCCESS) { if (b_info.reserved && size == (SHARED_TEXT_REGION_SIZE) && tasks_info.virtual_size > (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE)) { tasks_info.virtual_size -= (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE); } } } else { if (! pid_exists(pid) ) { return NoSuchProcess(); } // pid exists, so return AccessDenied error since task_for_pid() failed return AccessDenied(); } return Py_BuildValue("(ll)", tasks_info.resident_size, tasks_info.virtual_size); }
/* * Return a Python tuple (user_time, kernel_time) */ static PyObject* get_cpu_times(PyObject* self, PyObject* args) { long pid; int err; unsigned int info_count = TASK_BASIC_INFO_COUNT; task_port_t task; // = (task_port_t)NULL; time_value_t user_time, system_time; struct task_basic_info tasks_info; struct task_thread_times_info task_times; if (! PyArg_ParseTuple(args, "l", &pid)) { return NULL; } /* task_for_pid() requires special privileges * "This function can be called only if the process is owned by the * procmod group or if the caller is root." * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html */ err = task_for_pid(mach_task_self(), pid, &task); if ( err == KERN_SUCCESS) { info_count = TASK_BASIC_INFO_COUNT; err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); if (err != KERN_SUCCESS) { // errcode 4 is "invalid argument" (access denied) if (err == 4) { return AccessDenied(); } // otherwise throw a runtime error with appropriate error code return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_INFO) failed"); } info_count = TASK_THREAD_TIMES_INFO_COUNT; err = task_info(task, TASK_THREAD_TIMES_INFO, (task_info_t)&task_times, &info_count); if (err != KERN_SUCCESS) { // errcode 4 is "invalid argument" (access denied) if (err == 4) { return AccessDenied(); } return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_INFO) failed"); } } else { // task_for_pid failed if (! pid_exists(pid) ) { return NoSuchProcess(); } // pid exists, so return AccessDenied error since task_for_pid() failed return AccessDenied(); } float user_t = -1.0; float sys_t = -1.0; user_time = tasks_info.user_time; system_time = tasks_info.system_time; time_value_add(&user_time, &task_times.user_time); time_value_add(&system_time, &task_times.system_time); user_t = (float)user_time.seconds + ((float)user_time.microseconds / 1000000.0); sys_t = (float)system_time.seconds + ((float)system_time.microseconds / 1000000.0); return Py_BuildValue("(dd)", user_t, sys_t); }
void OS_get_table() { /* dir walker storage */ DIR *dir; struct dirent *dir_ent, *dir_result; /* all our storage is going to be here */ struct obstack mem_pool; /* container for scaped process values */ struct procstat *prs; /* string containing our local copy of format_str, elements will be * lower cased if we are able to figure them out */ char *format_str; /* initlize a small memory pool for this function */ obstack_init(&mem_pool); /* put the dirent on the obstack, since it's rather large */ dir_ent = obstack_alloc(&mem_pool, sizeof(struct dirent)); if ((dir = opendir("/proc")) == NULL) return; /* Iterate through all the process entries (numeric) under /proc */ while(readdir_r(dir, dir_ent, &dir_result) == 0 && dir_result) { /* Only look at this file if it's a proc id; that is, all numbers */ if(!is_pid(dir_result->d_name)) continue; /* allocate container for storing process values */ prs = obstack_alloc(&mem_pool, sizeof(struct procstat)); bzero(prs, sizeof(struct procstat)); /* intilize the format string */ obstack_printf(&mem_pool, get_string(STR_DEFAULT_FORMAT)); obstack_1grow(&mem_pool, '\0'); format_str = (char *) obstack_finish(&mem_pool); /* get process' uid/guid */ get_user_info(dir_result->d_name, format_str, prs, &mem_pool); /* scrape /proc/${pid}/stat */ if (get_proc_stat(dir_result->d_name, format_str, prs, &mem_pool) == false) { /* did the pid directory go away mid flight? */ if (pid_exists(dir_result->d_name, &mem_pool) == false) continue; } /* correct values (times) found in /proc/${pid}/stat */ fixup_stat_values(format_str, prs); /* get process' cmndline */ get_proc_cmndline(dir_result->d_name, format_str, prs, &mem_pool); /* get process' cwd & exec values from the symblink */ eval_link(dir_result->d_name, "cwd", F_CWD, &prs->cwd, format_str, &mem_pool); eval_link(dir_result->d_name, "exe", F_EXEC, &prs->exec, format_str, &mem_pool); /* scapre from /proc/{$pid}/status */ get_proc_status(dir_result->d_name, format_str, prs, &mem_pool); /* calculate precent cpu & mem values */ calc_prec(format_str, prs, &mem_pool); /* Go ahead and bless into a perl object */ bless_into_proc(format_str, field_names, prs->uid, prs->gid, prs->pid, prs->comm, prs->ppid, prs->pgrp, prs->sid, prs->tty, prs->flags, prs->minflt, prs->cminflt, prs->majflt, prs->cmajflt, prs->utime, prs->stime, prs->cutime, prs->cstime, prs->priority, prs->start_time, prs->vsize, prs->rss, prs->wchan, prs->time, prs->ctime, prs->state, prs->euid, prs->suid, prs->fuid, prs->egid, prs->sgid, prs->fgid, prs->pctcpu, prs->pctmem, prs->cmndline, prs->exec, prs->cwd ); /* we want a new prs, for the next itteration */ obstack_free(&mem_pool, prs); } closedir(dir); /* free all our tempoary memory */ obstack_free(&mem_pool, NULL); }