| Index: third_party/psutil/psutil/arch/osx/process_info.c
|
| diff --git a/third_party/psutil/psutil/arch/osx/process_info.c b/third_party/psutil/psutil/arch/osx/process_info.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..53b04098f67066104d784969d88af7cbd3a0d607
|
| --- /dev/null
|
| +++ b/third_party/psutil/psutil/arch/osx/process_info.c
|
| @@ -0,0 +1,301 @@
|
| +/*
|
| + * $Id: process_info.c 772 2010-11-03 13:51:11Z g.rodola $
|
| + *
|
| + * Helper functions related to fetching process information. Used by _psutil_osx
|
| + * module methods.
|
| + */
|
| +
|
| +#include <Python.h>
|
| +#include <assert.h>
|
| +#include <errno.h>
|
| +#include <limits.h> /* for INT_MAX */
|
| +#include <stdbool.h>
|
| +#include <stdlib.h>
|
| +#include <stdio.h>
|
| +#include <sys/sysctl.h>
|
| +
|
| +#include "process_info.h"
|
| +
|
| +#define ARGS_ACCESS_DENIED -2
|
| +
|
| +
|
| +
|
| +/*
|
| + * Returns a list of all BSD processes on the system. This routine
|
| + * allocates the list and puts it in *procList and a count of the
|
| + * number of entries in *procCount. You are responsible for freeing
|
| + * this list (use "free" from System framework).
|
| + * On success, the function returns 0.
|
| + * On error, the function returns a BSD errno value.
|
| + */
|
| +int
|
| +get_proc_list(kinfo_proc **procList, size_t *procCount)
|
| +{
|
| + /* Declaring mib as const requires use of a cast since the
|
| + * sysctl prototype doesn't include the const modifier. */
|
| + static const int mib3[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
|
| + size_t size, size2;
|
| + void *ptr;
|
| + int err, lim = 8; /* some limit */
|
| +
|
| + assert( procList != NULL);
|
| + assert(*procList == NULL);
|
| + assert(procCount != NULL);
|
| +
|
| + *procCount = 0;
|
| +
|
| + /* We start by calling sysctl with ptr == NULL and size == 0.
|
| + * That will succeed, and set size to the appropriate length.
|
| + * We then allocate a buffer of at least that size and call
|
| + * sysctl with that buffer. If that succeeds, we're done.
|
| + * If that call fails with ENOMEM, we throw the buffer away
|
| + * and try again.
|
| + * Note that the loop calls sysctl with NULL again. This is
|
| + * is necessary because the ENOMEM failure case sets size to
|
| + * the amount of data returned, not the amount of data that
|
| + * could have been returned.
|
| + */
|
| + while (lim-- > 0) {
|
| + size = 0;
|
| + if (sysctl((int *)mib3, 3, NULL, &size, NULL, 0) == -1) {
|
| + return errno;
|
| + }
|
| +
|
| + size2 = size + (size >> 3); /* add some */
|
| + if (size2 > size) {
|
| + ptr = malloc(size2);
|
| + if (ptr == NULL) {
|
| + ptr = malloc(size);
|
| + } else {
|
| + size = size2;
|
| + }
|
| + }
|
| + else {
|
| + ptr = malloc(size);
|
| + }
|
| + if (ptr == NULL) {
|
| + return ENOMEM;
|
| + }
|
| +
|
| + if (sysctl((int *)mib3, 3, ptr, &size, NULL, 0) == -1) {
|
| + err = errno;
|
| + free(ptr);
|
| + if (err != ENOMEM) {
|
| + return err;
|
| + }
|
| +
|
| + } else {
|
| + *procList = (kinfo_proc *)ptr;
|
| + *procCount = size / sizeof(kinfo_proc);
|
| + return 0;
|
| + }
|
| + }
|
| + return ENOMEM;
|
| +}
|
| +
|
| +
|
| +/*
|
| + * Modified from psi Python System Information project
|
| + *
|
| + * Get command path, arguments and environment variables.
|
| + *
|
| + * Based on code from ps.
|
| + *
|
| + * Returns:
|
| + * 0 for success
|
| + * 1 for sysctl error, errno exception raised
|
| + * -1 for failure, system or memory exception raised
|
| + * -2 rather ARGS_ACCESS_DENIED, for insufficient privileges
|
| + */
|
| +int
|
| +getcmdargs(long pid, PyObject **exec_path, PyObject **envlist, PyObject **arglist)
|
| +{
|
| + int nargs, mib[3];
|
| + size_t size, argmax;
|
| + char *curr_arg, *start_args, *iter_args, *end_args;
|
| + char *procargs = NULL;
|
| + char *err = NULL;
|
| + PyObject *arg;
|
| +
|
| + *arglist = Py_BuildValue("[]"); /* empty list */
|
| + if (*arglist == NULL) {
|
| + err = "getcmdargs(): arglist exception";
|
| + goto ERROR_RETURN;
|
| + }
|
| +
|
| + /* Get the maximum process arguments size. */
|
| + mib[0] = CTL_KERN;
|
| + mib[1] = KERN_ARGMAX;
|
| +
|
| + size = sizeof(argmax);
|
| + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) {
|
| + PyErr_SetFromErrno(NULL);
|
| + return errno;
|
| + }
|
| +
|
| + /* Allocate space for the arguments. */
|
| + procargs = (char *)malloc(argmax);
|
| + if (procargs == NULL) {
|
| + PyErr_SetString(PyExc_MemoryError,
|
| + "getcmdargs(): insufficient memory for procargs");
|
| + return ENOMEM;
|
| + }
|
| +
|
| + /*
|
| + * Make a sysctl() call to get the raw argument space of the process.
|
| + */
|
| + mib[0] = CTL_KERN;
|
| + mib[1] = KERN_PROCARGS2;
|
| + mib[2] = (int)pid;
|
| +
|
| + size = argmax;
|
| + if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
|
| + if (EINVAL == errno) { // invalid == access denied for some reason
|
| + free(procargs);
|
| + return ARGS_ACCESS_DENIED; /* Insufficient privileges */
|
| + }
|
| +
|
| + PyErr_SetFromErrno(PyExc_OSError);
|
| + free(procargs);
|
| + return errno;
|
| + }
|
| +
|
| + // copy the number of argument to nargs
|
| + memcpy(&nargs, procargs, sizeof(nargs));
|
| + iter_args = procargs + sizeof(nargs);
|
| + end_args = &procargs[size]; // end of the argument space
|
| + if (iter_args >= end_args) {
|
| + err = "getcmdargs(): argument length mismatch";
|
| + goto ERROR_RETURN;
|
| + }
|
| +
|
| + // Save the path
|
| + if (NULL != exec_path) {
|
| + *exec_path = Py_BuildValue("s", iter_args);
|
| + if (*exec_path == NULL) {
|
| + err = "getcmdargs(): exec_path exception";
|
| + goto ERROR_RETURN;
|
| + }
|
| + }
|
| +
|
| + //TODO: save the environment variables to envlist as well
|
| + // Skip over the exec_path and '\0' characters.
|
| + while (iter_args < end_args && *iter_args != '\0') { iter_args++; }
|
| + while (iter_args < end_args && *iter_args == '\0') { iter_args++; }
|
| +
|
| + /* Iterate through the '\0'-terminated strings and add each string
|
| + * to the Python List arglist as a Python string.
|
| + * Stop when nargs strings have been extracted. That should be all
|
| + * the arguments. The rest of the strings will be environment
|
| + * strings for the command.
|
| + */
|
| + curr_arg = iter_args;
|
| + start_args = iter_args; //reset start position to beginning of cmdline
|
| + while (iter_args < end_args && nargs > 0) {
|
| + if (*iter_args++ == '\0') {
|
| + /* Fetch next argument */
|
| + arg = Py_BuildValue("s", curr_arg);
|
| + if (arg == NULL) {
|
| + err = "getcmdargs(): exception building argument string";
|
| + goto ERROR_RETURN;
|
| + }
|
| + PyList_Append(*arglist, arg);
|
| + Py_DECREF(arg);
|
| +
|
| + curr_arg = iter_args;
|
| + nargs--;
|
| + }
|
| + }
|
| +
|
| + /*
|
| + * curr_arg position should be further than the start of the argspace
|
| + * and number of arguments should be 0 after iterating above. Otherwise
|
| + * we had an empty argument space or a missing terminating \0 etc.
|
| + */
|
| + if (curr_arg == start_args || nargs > 0) {
|
| + err = "getcmdargs(): argument parsing failed";
|
| + goto ERROR_RETURN;
|
| + }
|
| +
|
| +ERROR_RETURN:
|
| + // Clean up.
|
| + if (NULL != procargs) {
|
| + free(procargs);
|
| + }
|
| + if (NULL != err) {
|
| + PyErr_SetString(PyExc_SystemError, err);
|
| + return -1;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +
|
| +/* return process args as a python list */
|
| +PyObject*
|
| +get_arg_list(long pid)
|
| +{
|
| + int r;
|
| + PyObject *argList;
|
| + PyObject *env = NULL;
|
| + PyObject *args = NULL;
|
| + PyObject *exec_path = NULL;
|
| +
|
| + //special case for PID 0 (kernel_task) where cmdline cannot be fetched
|
| + if (pid == 0) {
|
| + return Py_BuildValue("[]");
|
| + }
|
| +
|
| + /* Fetch the command-line arguments and environment variables */
|
| + //printf("pid: %ld\n", pid);
|
| + if (pid < 0 || pid > (long)INT_MAX) {
|
| + return Py_BuildValue("");
|
| + }
|
| +
|
| + r = getcmdargs(pid, &exec_path, &env, &args);
|
| + if (r == 0) {
|
| + //PySequence_Tuple(args);
|
| + argList = PySequence_List(args);
|
| + }
|
| + else if (r == ARGS_ACCESS_DENIED) { //-2
|
| + argList = Py_BuildValue("[]");
|
| + }
|
| + else {
|
| + argList = Py_BuildValue("");
|
| + }
|
| +
|
| + Py_XDECREF(args);
|
| + Py_XDECREF(exec_path);
|
| + Py_XDECREF(env);
|
| + return argList;
|
| +}
|
| +
|
| +
|
| +int
|
| +get_kinfo_proc(pid_t pid, struct kinfo_proc *kp)
|
| +{
|
| + int mib[4];
|
| + size_t len;
|
| + mib[0] = CTL_KERN;
|
| + mib[1] = KERN_PROC;
|
| + mib[2] = KERN_PROC_PID;
|
| + mib[3] = pid;
|
| +
|
| + // fetch the info with sysctl()
|
| + len = sizeof(struct kinfo_proc);
|
| +
|
| + // now read the data from sysctl
|
| + if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) {
|
| + // raise an exception and throw errno as the error
|
| + PyErr_SetFromErrno(PyExc_OSError);
|
| + }
|
| +
|
| + /*
|
| + * sysctl succeeds but len is zero, happens when process has gone away
|
| + */
|
| + if (len == 0) {
|
| + errno = ESRCH;
|
| + PyErr_SetFromErrno(PyExc_OSError);
|
| + return -1;
|
| + }
|
| + return 0;
|
| +}
|
|
|