Extend libprocstat with functions to retrieve process command line

arguments and environment variables.

Suggested by:	stas
Reviewed by:	jhb and stas (initial version)
MFC after:	1 month
This commit is contained in:
Mikolaj Golub 2013-04-20 08:07:04 +00:00
parent 66e2f9998b
commit 4482b5e320
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=249679
7 changed files with 413 additions and 0 deletions

View file

@ -16,9 +16,13 @@ FBSD_1.2 {
};
FBSD_1.3 {
procstat_freeargv;
procstat_freeenvv;
procstat_freegroups;
procstat_freevmmap;
procstat_get_shm_info;
procstat_getargv;
procstat_getenvv;
procstat_getgroups;
procstat_getosrel;
procstat_getpathname;

View file

@ -28,6 +28,7 @@
#include <sys/param.h>
#include <sys/elf.h>
#include <sys/exec.h>
#include <sys/user.h>
#include <assert.h>
@ -56,6 +57,10 @@ struct procstat_core
static bool core_offset(struct procstat_core *core, off_t offset);
static bool core_read(struct procstat_core *core, void *buf, size_t len);
static ssize_t core_read_mem(struct procstat_core *core, void *buf,
size_t len, vm_offset_t addr, bool readall);
static void *get_args(struct procstat_core *core, vm_offset_t psstrings,
enum psc_type type, void *buf, size_t *lenp);
struct procstat_core *
procstat_core_open(const char *filename)
@ -146,6 +151,7 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf,
{
Elf_Note nhdr;
off_t offset, eoffset;
vm_offset_t psstrings;
void *freebuf;
size_t len;
u_int32_t n_type;
@ -183,6 +189,12 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf,
n_type = NT_PROCSTAT_OSREL;
structsize = sizeof(int);
break;
case PSC_TYPE_PSSTRINGS:
case PSC_TYPE_ARGV:
case PSC_TYPE_ENVV:
n_type = NT_PROCSTAT_PSSTRINGS;
structsize = sizeof(vm_offset_t);
break;
default:
warnx("unknown core stat type: %d", type);
return (NULL);
@ -238,6 +250,19 @@ procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf,
free(freebuf);
return (NULL);
}
if (type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV) {
if (len < sizeof(psstrings)) {
free(freebuf);
return (NULL);
}
psstrings = *(vm_offset_t *)buf;
if (freebuf == NULL)
len = *lenp;
else
buf = NULL;
free(freebuf);
buf = get_args(core, psstrings, type, buf, &len);
}
*lenp = len;
return (buf);
}
@ -276,3 +301,128 @@ core_read(struct procstat_core *core, void *buf, size_t len)
}
return (true);
}
static ssize_t
core_read_mem(struct procstat_core *core, void *buf, size_t len,
vm_offset_t addr, bool readall)
{
GElf_Phdr phdr;
off_t offset;
int i;
assert(core->pc_magic == PROCSTAT_CORE_MAGIC);
for (i = 0; i < core->pc_ehdr.e_phnum; i++) {
if (gelf_getphdr(core->pc_elf, i, &phdr) != &phdr) {
warnx("gelf_getphdr: %s", elf_errmsg(-1));
return (-1);
}
if (phdr.p_type != PT_LOAD)
continue;
if (addr < phdr.p_vaddr || addr > phdr.p_vaddr + phdr.p_memsz)
continue;
offset = phdr.p_offset + (addr - phdr.p_vaddr);
if ((phdr.p_vaddr + phdr.p_memsz) - addr < len) {
if (readall) {
warnx("format error: "
"attempt to read out of segment");
return (-1);
}
len = (phdr.p_vaddr + phdr.p_memsz) - addr;
}
if (!core_offset(core, offset))
return (-1);
if (!core_read(core, buf, len))
return (-1);
return (len);
}
warnx("format error: address %ju not found", (uintmax_t)addr);
return (-1);
}
#define ARGS_CHUNK_SZ 256 /* Chunk size (bytes) for get_args operations. */
static void *
get_args(struct procstat_core *core, vm_offset_t psstrings, enum psc_type type,
void *args, size_t *lenp)
{
struct ps_strings pss;
void *freeargs;
vm_offset_t addr;
char **argv, *p;
size_t chunksz, done, len, nchr, size;
ssize_t n;
u_int i, nstr;
assert(type == PSC_TYPE_ARGV || type == PSC_TYPE_ENVV);
if (core_read_mem(core, &pss, sizeof(pss), psstrings, true) == -1)
return (NULL);
if (type == PSC_TYPE_ARGV) {
addr = (vm_offset_t)pss.ps_argvstr;
nstr = pss.ps_nargvstr;
} else /* type == PSC_TYPE_ENVV */ {
addr = (vm_offset_t)pss.ps_envstr;
nstr = pss.ps_nenvstr;
}
if (addr == 0 || nstr == 0)
return (NULL);
if (nstr > ARG_MAX) {
warnx("format error");
return (NULL);
}
size = nstr * sizeof(char *);
argv = malloc(size);
if (argv == NULL) {
warn("malloc(%zu)", size);
return (NULL);
}
done = 0;
freeargs = NULL;
if (core_read_mem(core, argv, size, addr, true) == -1)
goto fail;
if (args != NULL) {
nchr = MIN(ARG_MAX, *lenp);
} else {
nchr = ARG_MAX;
freeargs = args = malloc(nchr);
if (args == NULL) {
warn("malloc(%zu)", nchr);
goto fail;
}
}
p = args;
for (i = 0; ; i++) {
if (i == nstr)
goto done;
/*
* The program may have scribbled into its argv array, e.g. to
* remove some arguments. If that has happened, break out
* before trying to read from NULL.
*/
if (argv[i] == NULL)
goto done;
for (addr = (vm_offset_t)argv[i]; ; addr += chunksz) {
chunksz = MIN(ARGS_CHUNK_SZ, nchr - 1 - done);
if (chunksz <= 0)
goto done;
n = core_read_mem(core, p, chunksz, addr, false);
if (n == -1)
goto fail;
len = strnlen(p, chunksz);
p += len;
done += len;
if (len != chunksz)
break;
}
*p++ = '\0';
done++;
}
fail:
free(freeargs);
args = NULL;
done:
*lenp = done;
free(argv);
return (args);
}

View file

@ -37,6 +37,9 @@ enum psc_type {
PSC_TYPE_UMASK,
PSC_TYPE_RLIMIT,
PSC_TYPE_OSREL,
PSC_TYPE_PSSTRINGS,
PSC_TYPE_ARGV,
PSC_TYPE_ENVV,
};
struct procstat_core;

View file

@ -32,6 +32,8 @@
.Nm procstat_open_kvm ,
.Nm procstat_open_sysctl ,
.Nm procstat_close ,
.Nm procstat_getargv ,
.Nm procstat_getenvv ,
.Nm procstat_getfiles ,
.Nm procstat_getgroups ,
.Nm procstat_getosrel ,
@ -39,6 +41,8 @@
.Nm procstat_getprocs ,
.Nm procstat_getumask ,
.Nm procstat_getvmmap ,
.Nm procstat_freeargv ,
.Nm procstat_freeenvv ,
.Nm procstat_freefiles ,
.Nm procstat_freegroups ,
.Nm procstat_freeprocs ,
@ -59,6 +63,14 @@
.Fn procstat_close "struct procstat *procstat"
.Fc
.Ft void
.Fo procstat_freeargv
.Fa "struct procstat *procstat"
.Fc
.Ft void
.Fo procstat_freeenvv
.Fa "struct procstat *procstat"
.Fc
.Ft void
.Fo procstat_freefiles
.Fa "struct procstat *procstat"
.Fa "struct filestat_list *head"
@ -110,6 +122,20 @@
.Fa "struct vnstat *vn"
.Fa "char *errbuf"
.Fc
.Ft "char **"
.Fo procstat_getargv
.Fa "struct procstat *procstat"
.Fa "const struct kinfo_proc *kp"
.Fa "size_t nchr"
.Fa "char *errbuf"
.Fc
.Ft "char **"
.Fo procstat_getenvv
.Fa "struct procstat *procstat"
.Fa "const struct kinfo_proc *kp"
.Fa "size_t nchr"
.Fa "char *errbuf"
.Fc
.Ft "struct filestat_list *"
.Fo procstat_getfiles
.Fa "struct procstat *procstat"
@ -251,6 +277,50 @@ The caller is responsible to free the allocated memory with a subsequent
function call.
.Pp
The
.Fn procstat_getargv
function gets a pointer to the
.Vt procstat
structure from one of the
.Fn procstat_open_*
functions, a pointer to
.Vt kinfo_proc
structure from the array obtained from the
.Fn kvm_getprocs
function, and returns a null-terminated argument vector that corresponds to
the command line arguments passed to the process.
The
.Fa nchr
argument indicates the maximum number of characters, including null bytes,
to use in building the strings.
If this amount is exceeded, the string causing the overflow is truncated and
the partial result is returned.
This is handy for programs that print only a one line summary of a
command and should not copy out large amounts of text only to ignore it.
If
.Fa nchr
is zero, no limit is imposed and all argument strings are returned.
The values of the returned argument vector refer the strings stored
in the
.Vt procstat
internal buffer.
A subsequent call of the function with the same
.Vt procstat
argument will reuse the buffer.
To free the allocated memory
.Fn procstat_freeargv
function call can be used, or it will be released on
.Fn procstat_close .
.Pp
The
.Fn procstat_getenvv
function is similar to
.Fn procstat_getargv
but returns the vector of environment strings.
The caller may free the allocated memory with a subsequent
.Fn procstat_freeenv
function call.
.Pp
The
.Fn procstat_getfiles
function gets a pointer to the
.Vt procstat

View file

@ -105,6 +105,8 @@ int statfs(const char *, struct statfs *); /* XXX */
#define PROCSTAT_SYSCTL 2
#define PROCSTAT_CORE 3
static char **getargv(struct procstat *procstat, struct kinfo_proc *kp,
size_t nchr, int env);
static char *getmnton(kvm_t *kd, struct mount *m);
static struct kinfo_vmentry * kinfo_getvmmap_core(struct procstat_core *core,
int *cntp);
@ -158,6 +160,8 @@ procstat_close(struct procstat *procstat)
kvm_close(procstat->kd);
else if (procstat->type == PROCSTAT_CORE)
procstat_core_close(procstat->core);
procstat_freeargv(procstat);
procstat_freeenvv(procstat);
free(procstat);
}
@ -1524,6 +1528,180 @@ getmnton(kvm_t *kd, struct mount *m)
return (mt->mntonname);
}
/*
* Auxiliary structures and functions to get process environment or
* command line arguments.
*/
struct argvec {
char *buf;
size_t bufsize;
char **argv;
size_t argc;
};
static struct argvec *
argvec_alloc(size_t bufsize)
{
struct argvec *av;
av = malloc(sizeof(*av));
if (av == NULL)
return (NULL);
av->bufsize = bufsize;
av->buf = malloc(av->bufsize);
if (av->buf == NULL) {
free(av);
return (NULL);
}
av->argc = 32;
av->argv = malloc(sizeof(char *) * av->argc);
if (av->argv == NULL) {
free(av->buf);
free(av);
return (NULL);
}
return av;
}
static void
argvec_free(struct argvec * av)
{
free(av->argv);
free(av->buf);
free(av);
}
static char **
getargv(struct procstat *procstat, struct kinfo_proc *kp, size_t nchr, int env)
{
int error, name[4], argc, i;
struct argvec *av, **avp;
enum psc_type type;
size_t len;
char *p, **argv;
assert(procstat);
assert(kp);
if (procstat->type == PROCSTAT_KVM) {
warnx("can't use kvm access method");
return (NULL);
}
if (procstat->type != PROCSTAT_SYSCTL &&
procstat->type != PROCSTAT_CORE) {
warnx("unknown access method: %d", procstat->type);
return (NULL);
}
if (nchr == 0 || nchr > ARG_MAX)
nchr = ARG_MAX;
avp = (struct argvec **)(env ? &procstat->argv : &procstat->envv);
av = *avp;
if (av == NULL)
{
av = argvec_alloc(nchr);
if (av == NULL)
{
warn("malloc(%zu)", nchr);
return (NULL);
}
*avp = av;
} else if (av->bufsize < nchr) {
av->buf = reallocf(av->buf, nchr);
if (av->buf == NULL) {
warn("malloc(%zu)", nchr);
return (NULL);
}
}
if (procstat->type == PROCSTAT_SYSCTL) {
name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = env ? KERN_PROC_ENV : KERN_PROC_ARGS;
name[3] = kp->ki_pid;
len = nchr;
error = sysctl(name, 4, av->buf, &len, NULL, 0);
if (error != 0 && errno != ESRCH && errno != EPERM)
warn("sysctl(kern.proc.%s)", env ? "env" : "args");
if (error != 0 || len == 0)
return (NULL);
} else /* procstat->type == PROCSTAT_CORE */ {
type = env ? PSC_TYPE_ENVV : PSC_TYPE_ARGV;
len = nchr;
if (procstat_core_get(procstat->core, type, av->buf, &len)
== NULL) {
return (NULL);
}
}
argv = av->argv;
argc = av->argc;
i = 0;
for (p = av->buf; p < av->buf + len; p += strlen(p) + 1) {
argv[i++] = p;
if (i < argc)
continue;
/* Grow argv. */
argc += argc;
argv = realloc(argv, sizeof(char *) * argc);
if (argv == NULL) {
warn("malloc(%zu)", sizeof(char *) * argc);
return (NULL);
}
av->argv = argv;
av->argc = argc;
}
argv[i] = NULL;
return (argv);
}
/*
* Return process command line arguments.
*/
char **
procstat_getargv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr)
{
return (getargv(procstat, p, nchr, 0));
}
/*
* Free the buffer allocated by procstat_getargv().
*/
void
procstat_freeargv(struct procstat *procstat)
{
if (procstat->argv != NULL) {
argvec_free(procstat->argv);
procstat->argv = NULL;
}
}
/*
* Return process environment.
*/
char **
procstat_getenvv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr)
{
return (getargv(procstat, p, nchr, 1));
}
/*
* Free the buffer allocated by procstat_getenvv().
*/
void
procstat_freeenvv(struct procstat *procstat)
{
if (procstat->envv != NULL) {
argvec_free(procstat->envv);
procstat->envv = NULL;
}
}
static struct kinfo_vmentry *
kinfo_getvmmap_core(struct procstat_core *core, int *cntp)
{

View file

@ -147,6 +147,8 @@ STAILQ_HEAD(filestat_list, filestat);
__BEGIN_DECLS
void procstat_close(struct procstat *procstat);
void procstat_freeargv(struct procstat *procstat);
void procstat_freeenvv(struct procstat *procstat);
void procstat_freegroups(struct procstat *procstat, gid_t *groups);
void procstat_freeprocs(struct procstat *procstat, struct kinfo_proc *p);
void procstat_freefiles(struct procstat *procstat,
@ -167,6 +169,10 @@ int procstat_get_socket_info(struct procstat *procstat, struct filestat *fst,
struct sockstat *sock, char *errbuf);
int procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst,
struct vnstat *vn, char *errbuf);
char **procstat_getargv(struct procstat *procstat, struct kinfo_proc *p,
size_t nchr);
char **procstat_getenvv(struct procstat *procstat, struct kinfo_proc *p,
size_t nchr);
gid_t *procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp,
unsigned int *count);
int procstat_getosrel(struct procstat *procstat, struct kinfo_proc *kp,

View file

@ -34,6 +34,8 @@ struct procstat {
kvm_t *kd;
void *vmentries;
void *files;
void *argv;
void *envv;
struct procstat_core *core;
};