Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 20 Apr 2013 08:07:04 +0000 (UTC)
From:      Mikolaj Golub <trociny@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r249679 - head/lib/libprocstat
Message-ID:  <201304200807.r3K874GB008895@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: trociny
Date: Sat Apr 20 08:07:04 2013
New Revision: 249679
URL: http://svnweb.freebsd.org/changeset/base/249679

Log:
  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

Modified:
  head/lib/libprocstat/Symbol.map
  head/lib/libprocstat/core.c
  head/lib/libprocstat/core.h
  head/lib/libprocstat/libprocstat.3
  head/lib/libprocstat/libprocstat.c
  head/lib/libprocstat/libprocstat.h
  head/lib/libprocstat/libprocstat_internal.h

Modified: head/lib/libprocstat/Symbol.map
==============================================================================
--- head/lib/libprocstat/Symbol.map	Sat Apr 20 08:05:04 2013	(r249678)
+++ head/lib/libprocstat/Symbol.map	Sat Apr 20 08:07:04 2013	(r249679)
@@ -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;

Modified: head/lib/libprocstat/core.c
==============================================================================
--- head/lib/libprocstat/core.c	Sat Apr 20 08:05:04 2013	(r249678)
+++ head/lib/libprocstat/core.c	Sat Apr 20 08:07:04 2013	(r249679)
@@ -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 *
 {
 	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 *
 		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 *
 			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, vo
 	}
 	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);
+}

Modified: head/lib/libprocstat/core.h
==============================================================================
--- head/lib/libprocstat/core.h	Sat Apr 20 08:05:04 2013	(r249678)
+++ head/lib/libprocstat/core.h	Sat Apr 20 08:07:04 2013	(r249679)
@@ -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;

Modified: head/lib/libprocstat/libprocstat.3
==============================================================================
--- head/lib/libprocstat/libprocstat.3	Sat Apr 20 08:05:04 2013	(r249678)
+++ head/lib/libprocstat/libprocstat.3	Sat Apr 20 08:07:04 2013	(r249679)
@@ -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 al
 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

Modified: head/lib/libprocstat/libprocstat.c
==============================================================================
--- head/lib/libprocstat/libprocstat.c	Sat Apr 20 08:05:04 2013	(r249678)
+++ head/lib/libprocstat/libprocstat.c	Sat Apr 20 08:07:04 2013	(r249679)
@@ -105,6 +105,8 @@ int     statfs(const char *, struct stat
 #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)
 {

Modified: head/lib/libprocstat/libprocstat.h
==============================================================================
--- head/lib/libprocstat/libprocstat.h	Sat Apr 20 08:05:04 2013	(r249678)
+++ head/lib/libprocstat/libprocstat.h	Sat Apr 20 08:07:04 2013	(r249679)
@@ -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 proc
     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,

Modified: head/lib/libprocstat/libprocstat_internal.h
==============================================================================
--- head/lib/libprocstat/libprocstat_internal.h	Sat Apr 20 08:05:04 2013	(r249678)
+++ head/lib/libprocstat/libprocstat_internal.h	Sat Apr 20 08:07:04 2013	(r249679)
@@ -34,6 +34,8 @@ struct procstat {
 	kvm_t	*kd;
 	void	*vmentries;
 	void	*files;
+	void	*argv;
+	void	*envv;
 	struct procstat_core *core;
 };
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201304200807.r3K874GB008895>