Skip to content

Commit

Permalink
Fix #1276: [AIX] use getargs to get process cmdline (#1500) (patch by
Browse files Browse the repository at this point in the history
  • Loading branch information
wiggin15 authored and giampaolo committed May 6, 2019
1 parent 23832cd commit 7d8c23d
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 33 deletions.
18 changes: 7 additions & 11 deletions psutil/_psaix.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,20 +358,13 @@ def __init__(self, pid):
self._procfs_path = get_procfs_path()

def oneshot_enter(self):
self._proc_name_and_args.cache_activate(self)
self._proc_basic_info.cache_activate(self)
self._proc_cred.cache_activate(self)

def oneshot_exit(self):
self._proc_name_and_args.cache_deactivate(self)
self._proc_basic_info.cache_deactivate(self)
self._proc_cred.cache_deactivate(self)

@wrap_exceptions
@memoize_when_activated
def _proc_name_and_args(self):
return cext.proc_name_and_args(self.pid, self._procfs_path)

@wrap_exceptions
@memoize_when_activated
def _proc_basic_info(self):
Expand All @@ -386,14 +379,17 @@ def _proc_cred(self):
def name(self):
if self.pid == 0:
return "swapper"
# note: this is limited to 15 characters
return self._proc_name_and_args()[0].rstrip("\x00")
# note: max 16 characters
return cext.proc_name(self.pid, self._procfs_path).rstrip("\x00")

@wrap_exceptions
def exe(self):
# there is no way to get executable path in AIX other than to guess,
# and guessing is more complex than what's in the wrapping class
exe = self.cmdline()[0]
cmdline = self.cmdline()
if not cmdline:
return ''
exe = cmdline[0]
if os.path.sep in exe:
# relative or absolute path
if not os.path.isabs(exe):
Expand All @@ -415,7 +411,7 @@ def exe(self):

@wrap_exceptions
def cmdline(self):
return self._proc_name_and_args()[1].split(' ')
return cext.proc_args(self.pid)

@wrap_exceptions
def create_time(self):
Expand Down
86 changes: 64 additions & 22 deletions psutil/_psutil_aix.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,21 @@
*
* Useful resources:
* - proc filesystem: http://www-01.ibm.com/support/knowledgecenter/
* ssw_aix_61/com.ibm.aix.files/proc.htm
* ssw_aix_72/com.ibm.aix.files/proc.htm
* - libperfstat: http://www-01.ibm.com/support/knowledgecenter/
* ssw_aix_61/com.ibm.aix.files/libperfstat.h.htm
* ssw_aix_72/com.ibm.aix.files/libperfstat.h.htm
*/

#include <Python.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/limits.h>
#include <sys/proc.h>
#include <sys/sysinfo.h>
#include <sys/procfs.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sysinfo.h>
#include <sys/thread.h>
#include <sys/types.h>
#include <fcntl.h>
#include <utmp.h>
#include <utmpx.h>
Expand Down Expand Up @@ -134,41 +135,80 @@ psutil_proc_basic_info(PyObject *self, PyObject *args) {


/*
* Return process name and args as a Python tuple.
* Return process name as a Python string.
*/
static PyObject *
psutil_proc_name_and_args(PyObject *self, PyObject *args) {
psutil_proc_name(PyObject *self, PyObject *args) {
int pid;
char path[100];
psinfo_t info;
const char *procfs_path;
PyObject *py_name = NULL;
PyObject *py_args = NULL;
PyObject *py_retlist = NULL;

if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
return NULL;
sprintf(path, "%s/%i/psinfo", procfs_path, pid);
if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
return NULL;

py_name = PyUnicode_DecodeFSDefault(info.pr_fname);
if (!py_name)
return PyUnicode_DecodeFSDefaultAndSize(info.pr_fname, PRFNSZ);
}


/*
* Return process command line arguments as a Python list
*/
static PyObject *
psutil_proc_args(PyObject *self, PyObject *args) {
int pid;
PyObject *py_retlist = PyList_New(0);
PyObject *py_arg = NULL;
struct procsinfo procbuf;
long arg_max;
char *argbuf = NULL;
char *curarg = NULL;
int ret;

if (py_retlist == NULL)
return NULL;
if (!PyArg_ParseTuple(args, "i", &pid))
goto error;
py_args = PyUnicode_DecodeFSDefault(info.pr_psargs);
if (!py_args)
arg_max = sysconf(_SC_ARG_MAX);
argbuf = malloc(arg_max);
if (argbuf == NULL) {
PyErr_NoMemory();
goto error;
py_retlist = Py_BuildValue("OO", py_name, py_args);
if (!py_retlist)
}

procbuf.pi_pid = pid;
ret = getargs(&procbuf, sizeof(struct procinfo), argbuf, ARG_MAX);
if (ret == -1) {
PyErr_SetFromErrno(PyExc_OSError);
goto error;
Py_DECREF(py_name);
Py_DECREF(py_args);
}

curarg = argbuf;
/* getargs will always append an extra NULL to end the arg list,
* even if the buffer is not big enough (even though it is supposed
* to be) so the following 'while' is safe */
while (*curarg != '\0') {
py_arg = PyUnicode_DecodeFSDefault(curarg);
if (!py_arg)
goto error;
if (PyList_Append(py_retlist, py_arg))
goto error;
Py_DECREF(py_arg);
curarg = strchr(curarg, '\0') + 1;
}

free(argbuf);

return py_retlist;

error:
Py_XDECREF(py_name);
Py_XDECREF(py_args);
if (argbuf != NULL)
free(argbuf);
Py_XDECREF(py_retlist);
Py_XDECREF(py_arg);
return NULL;
}

Expand Down Expand Up @@ -880,8 +920,10 @@ 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_name", psutil_proc_name, METH_VARARGS,
"Return process name."},
{"proc_args", psutil_proc_args, METH_VARARGS,
"Return process command line arguments."},
{"proc_cpu_times", psutil_proc_cpu_times, METH_VARARGS,
"Return process user and system CPU times."},
{"proc_cred", psutil_proc_cred, METH_VARARGS,
Expand Down
16 changes: 16 additions & 0 deletions psutil/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,12 +724,28 @@ def test_cmdline(self):
else:
raise

def test_long_cmdline(self):
create_exe(TESTFN)
self.addCleanup(safe_rmpath, TESTFN)
cmdline = [TESTFN] + (["0123456789"] * 20)
sproc = get_test_subprocess(cmdline)
p = psutil.Process(sproc.pid)
self.assertEqual(p.cmdline(), cmdline)

def test_name(self):
sproc = get_test_subprocess(PYTHON_EXE)
name = psutil.Process(sproc.pid).name().lower()
pyexe = os.path.basename(os.path.realpath(sys.executable)).lower()
assert pyexe.startswith(name), (pyexe, name)

def test_long_name(self):
long_name = TESTFN + ("0123456789" * 2)
create_exe(long_name)
self.addCleanup(safe_rmpath, long_name)
sproc = get_test_subprocess(long_name)
p = psutil.Process(sproc.pid)
self.assertEqual(p.name(), os.path.basename(long_name))

# XXX
@unittest.skipIf(SUNOS, "broken on SUNOS")
@unittest.skipIf(AIX, "broken on AIX")
Expand Down

0 comments on commit 7d8c23d

Please sign in to comment.