summaryrefslogtreecommitdiffstats
path: root/lib/libprocstat
diff options
context:
space:
mode:
authortrociny <trociny@FreeBSD.org>2013-04-20 08:07:04 +0000
committertrociny <trociny@FreeBSD.org>2013-04-20 08:07:04 +0000
commit890cfdcd4f22a72faa1ac356436926ed16e08270 (patch)
tree1bc4877fd293d6e22b313d21587e331e2880b02b /lib/libprocstat
parent7b0f0126fb247c7c74a8723dc794fc93445f2009 (diff)
downloadFreeBSD-src-890cfdcd4f22a72faa1ac356436926ed16e08270.zip
FreeBSD-src-890cfdcd4f22a72faa1ac356436926ed16e08270.tar.gz
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
Diffstat (limited to 'lib/libprocstat')
-rw-r--r--lib/libprocstat/Symbol.map4
-rw-r--r--lib/libprocstat/core.c150
-rw-r--r--lib/libprocstat/core.h3
-rw-r--r--lib/libprocstat/libprocstat.370
-rw-r--r--lib/libprocstat/libprocstat.c178
-rw-r--r--lib/libprocstat/libprocstat.h6
-rw-r--r--lib/libprocstat/libprocstat_internal.h2
7 files changed, 413 insertions, 0 deletions
diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map
index 569c6f4..f2632c2 100644
--- a/lib/libprocstat/Symbol.map
+++ b/lib/libprocstat/Symbol.map
@@ -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;
diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c
index e750222..72dee88 100644
--- a/lib/libprocstat/core.c
+++ b/lib/libprocstat/core.c
@@ -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);
+}
diff --git a/lib/libprocstat/core.h b/lib/libprocstat/core.h
index 4d572b1..2361148 100644
--- a/lib/libprocstat/core.h
+++ b/lib/libprocstat/core.h
@@ -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;
diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3
index 9fecf87..e1dc987 100644
--- a/lib/libprocstat/libprocstat.3
+++ b/lib/libprocstat/libprocstat.3
@@ -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
diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c
index 6a71bf4..52b9409 100644
--- a/lib/libprocstat/libprocstat.c
+++ b/lib/libprocstat/libprocstat.c
@@ -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)
{
diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h
index 353948b..b7719c0 100644
--- a/lib/libprocstat/libprocstat.h
+++ b/lib/libprocstat/libprocstat.h
@@ -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,
diff --git a/lib/libprocstat/libprocstat_internal.h b/lib/libprocstat/libprocstat_internal.h
index ff1e742..6ca3197 100644
--- a/lib/libprocstat/libprocstat_internal.h
+++ b/lib/libprocstat/libprocstat_internal.h
@@ -34,6 +34,8 @@ struct procstat {
kvm_t *kd;
void *vmentries;
void *files;
+ void *argv;
+ void *envv;
struct procstat_core *core;
};
OpenPOWER on IntegriCloud