Code to get the env vars of a process
This commit is contained in:
parent
0dd3334811
commit
72e2307c16
@ -10,10 +10,7 @@ import kitty.fast_data_types as fast_data_types
|
|||||||
from .constants import is_macos, shell_path, terminfo_dir
|
from .constants import is_macos, shell_path, terminfo_dir
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from kitty.fast_data_types import cmdline_of_process as _cmdl, cwd_of_process as _cwd
|
from kitty.fast_data_types import cmdline_of_process, cwd_of_process as _cwd, environ_of_process as _environ_of_process
|
||||||
|
|
||||||
def cmdline_of_process(pid):
|
|
||||||
return _cmdl(pid)
|
|
||||||
|
|
||||||
def cwd_of_process(pid):
|
def cwd_of_process(pid):
|
||||||
return os.path.realpath(_cwd(pid))
|
return os.path.realpath(_cwd(pid))
|
||||||
@ -27,6 +24,36 @@ else:
|
|||||||
ans = '/proc/{}/cwd'.format(pid)
|
ans = '/proc/{}/cwd'.format(pid)
|
||||||
return os.path.realpath(ans)
|
return os.path.realpath(ans)
|
||||||
|
|
||||||
|
def _environ_of_process(pid):
|
||||||
|
return open('/proc/{}/environ'.format(pid), 'rb').read().decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_environ_block(data):
|
||||||
|
"""Parse a C environ block of environment variables into a dictionary."""
|
||||||
|
# The block is usually raw data from the target process. It might contain
|
||||||
|
# trailing garbage and lines that do not look like assignments.
|
||||||
|
ret = {}
|
||||||
|
pos = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
next_pos = data.find("\0", pos)
|
||||||
|
# nul byte at the beginning or double nul byte means finish
|
||||||
|
if next_pos <= pos:
|
||||||
|
break
|
||||||
|
# there might not be an equals sign
|
||||||
|
equal_pos = data.find("=", pos, next_pos)
|
||||||
|
if equal_pos > pos:
|
||||||
|
key = data[pos:equal_pos]
|
||||||
|
value = data[equal_pos + 1:next_pos]
|
||||||
|
ret[key] = value
|
||||||
|
pos = next_pos + 1
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def environ_of_process(pid):
|
||||||
|
return parse_environ_block(_environ_of_process(pid))
|
||||||
|
|
||||||
|
|
||||||
def remove_cloexec(fd):
|
def remove_cloexec(fd):
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.fcntl(fd, fcntl.F_GETFD) & ~fcntl.FD_CLOEXEC)
|
fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.fcntl(fd, fcntl.F_GETFD) & ~fcntl.FD_CLOEXEC)
|
||||||
@ -109,6 +136,13 @@ class Child:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return list(self.argv)
|
return list(self.argv)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def environ(self):
|
||||||
|
try:
|
||||||
|
return environ_of_process(self.pid)
|
||||||
|
except Exception:
|
||||||
|
return {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_cwd(self):
|
def current_cwd(self):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -133,9 +133,130 @@ error:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
environ_of_process(PyObject *self UNUSED, PyObject *pid_) {
|
||||||
|
// Taken from psutil, with thanks (BSD 3-clause license)
|
||||||
|
int mib[3];
|
||||||
|
int nargs;
|
||||||
|
char *procargs = NULL;
|
||||||
|
char *procenv = NULL;
|
||||||
|
char *arg_ptr;
|
||||||
|
char *arg_end;
|
||||||
|
char *env_start;
|
||||||
|
size_t argmax;
|
||||||
|
PyObject *py_ret = NULL;
|
||||||
|
if (!PyLong_Check(pid_)) { PyErr_SetString(PyExc_TypeError, "pid must be an int"); goto error; }
|
||||||
|
long pid = PyLong_AsLong(pid_);
|
||||||
|
if (pid < 0) { PyErr_SetString(PyExc_TypeError, "pid cannot be negative"); goto error; }
|
||||||
|
|
||||||
|
// special case for PID 0 (kernel_task) where cmdline cannot be fetched
|
||||||
|
if (pid == 0)
|
||||||
|
goto empty;
|
||||||
|
|
||||||
|
// read argmax and allocate memory for argument space.
|
||||||
|
argmax = get_argmax();
|
||||||
|
if (! argmax) {
|
||||||
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
procargs = (char *)malloc(argmax);
|
||||||
|
if (NULL == procargs) {
|
||||||
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read argument space
|
||||||
|
mib[0] = CTL_KERN;
|
||||||
|
mib[1] = KERN_PROCARGS2;
|
||||||
|
mib[2] = (pid_t)pid;
|
||||||
|
if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) {
|
||||||
|
// In case of zombie process or a non-existant process we'll get EINVAL
|
||||||
|
// to NSP and _psosx.py will translate it to ZP.
|
||||||
|
if (errno == EINVAL)
|
||||||
|
PyErr_Format(PyExc_ValueError, "process with pid %ld either does not exist or is a zombie", pid);
|
||||||
|
else
|
||||||
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_end = &procargs[argmax];
|
||||||
|
// copy the number of arguments to nargs
|
||||||
|
memcpy(&nargs, procargs, sizeof(nargs));
|
||||||
|
|
||||||
|
// skip executable path
|
||||||
|
arg_ptr = procargs + sizeof(nargs);
|
||||||
|
arg_ptr = memchr(arg_ptr, '\0', arg_end - arg_ptr);
|
||||||
|
|
||||||
|
if (arg_ptr == NULL || arg_ptr == arg_end)
|
||||||
|
goto empty;
|
||||||
|
|
||||||
|
// skip ahead to the first argument
|
||||||
|
for (; arg_ptr < arg_end; arg_ptr++) {
|
||||||
|
if (*arg_ptr != '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate through arguments
|
||||||
|
while (arg_ptr < arg_end && nargs > 0) {
|
||||||
|
if (*arg_ptr++ == '\0')
|
||||||
|
nargs--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build an environment variable block
|
||||||
|
env_start = arg_ptr;
|
||||||
|
|
||||||
|
procenv = calloc(1, arg_end - arg_ptr);
|
||||||
|
if (procenv == NULL) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*arg_ptr != '\0' && arg_ptr < arg_end) {
|
||||||
|
char *s = memchr(arg_ptr + 1, '\0', arg_end - arg_ptr);
|
||||||
|
|
||||||
|
if (s == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
memcpy(procenv + (arg_ptr - env_start), arg_ptr, s - arg_ptr);
|
||||||
|
|
||||||
|
arg_ptr = s + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
py_ret = PyUnicode_DecodeFSDefaultAndSize(
|
||||||
|
procenv, arg_ptr - env_start + 1);
|
||||||
|
if (!py_ret) {
|
||||||
|
// XXX: don't want to free() this as per:
|
||||||
|
// https://github.com/giampaolo/psutil/issues/926
|
||||||
|
// It sucks but not sure what else to do.
|
||||||
|
procargs = NULL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(procargs);
|
||||||
|
free(procenv);
|
||||||
|
|
||||||
|
return py_ret;
|
||||||
|
|
||||||
|
empty:
|
||||||
|
if (procargs != NULL)
|
||||||
|
free(procargs);
|
||||||
|
return Py_BuildValue("s", "");
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_XDECREF(py_ret);
|
||||||
|
if (procargs != NULL)
|
||||||
|
free(procargs);
|
||||||
|
if (procenv != NULL)
|
||||||
|
free(procargs);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
{"cwd_of_process", (PyCFunction)cwd_of_process, METH_O, ""},
|
{"cwd_of_process", (PyCFunction)cwd_of_process, METH_O, ""},
|
||||||
{"cmdline_of_process", (PyCFunction)cmdline_of_process, METH_O, ""},
|
{"cmdline_of_process", (PyCFunction)cmdline_of_process, METH_O, ""},
|
||||||
|
{"environ_of_process", (PyCFunction)environ_of_process, METH_O, ""},
|
||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user