From owner-svn-src-all@FreeBSD.ORG Sat Apr 20 08:07:06 2013 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id 2E94EEC6; Sat, 20 Apr 2013 08:07:06 +0000 (UTC) (envelope-from trociny@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 1F3391232; Sat, 20 Apr 2013 08:07:06 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.6/8.14.6) with ESMTP id r3K876Ze008923; Sat, 20 Apr 2013 08:07:06 GMT (envelope-from trociny@svn.freebsd.org) Received: (from trociny@localhost) by svn.freebsd.org (8.14.6/8.14.5/Submit) id r3K874GB008895; Sat, 20 Apr 2013 08:07:04 GMT (envelope-from trociny@svn.freebsd.org) Message-Id: <201304200807.r3K874GB008895@svn.freebsd.org> From: Mikolaj Golub Date: Sat, 20 Apr 2013 08:07:04 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r249679 - head/lib/libprocstat X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 20 Apr 2013 08:07:06 -0000 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 #include +#include #include #include @@ -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; };