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:
parent
7b0f0126fb
commit
890cfdcd4f
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -34,6 +34,8 @@ struct procstat {
|
||||
kvm_t *kd;
|
||||
void *vmentries;
|
||||
void *files;
|
||||
void *argv;
|
||||
void *envv;
|
||||
struct procstat_core *core;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user