From f8ee37be0ba8f7ba08ee7b84ecc08ad633525702 Mon Sep 17 00:00:00 2001 From: trociny Date: Tue, 21 May 2013 19:04:16 +0000 Subject: [PATCH] MFC r249666, r249667, r249670, r249672, r249674, r249676, r249677, r249679, r249681, r249684, r249688, r249711, r249731, r250146 r249666, r249682: Make libprocstat(3) extract procstat notes from a process core file. PR: kern/173723 Suggested by: jhb Glanced by: kib r249667: Add procstat_getvmmap function to get VM layout of a process. r249670: Add procstat_getgroups function to retrieve process groups. r249672: Add procstat_getumask function to retrieve a process umask. r249674: Add procstat_getrlimit function to retrieve a process resource limits info. r249676: Add procstat_getpathname function to retrieve a process executable. r249677: Add procstat_getosrel function to retrieve a process osrel info. r249679: Extend libprocstat with functions to retrieve process command line arguments and environment variables. Suggested by: stas Reviewed by: jhb and stas (initial version) r249681: Add procstat_getauxv function to retrieve a process auxiliary vector. r249684: Add procstat_getkstack function to dump kernel stacks of a process. r249688: Bump date. r249711 (joel): mdoc: end function context properly. r249731: Embed revision id in the library. r250146: KVM method support for procstat_getgroups, procstat_getumask, procstat_getrlimit, and procstat_getosrel. git-svn-id: svn://svn.freebsd.org/base/stable/9@250870 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- Makefile.inc1 | 2 +- lib/libprocstat/Makefile | 5 +- lib/libprocstat/Symbol.map | 17 + lib/libprocstat/core.c | 433 ++++++++++ lib/libprocstat/core.h | 53 ++ lib/libprocstat/libprocstat.3 | 272 ++++++- lib/libprocstat/libprocstat.c | 1037 +++++++++++++++++++++++- lib/libprocstat/libprocstat.h | 44 + lib/libprocstat/libprocstat_internal.h | 3 + 9 files changed, 1846 insertions(+), 20 deletions(-) create mode 100644 lib/libprocstat/core.c create mode 100644 lib/libprocstat/core.h diff --git a/Makefile.inc1 b/Makefile.inc1 index 123f842e2..119b7b7b1 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -1371,7 +1371,7 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} ${_kerberos5_lib_libhdb} \ ${_kerberos5_lib_libhx509} ${_kerberos5_lib_libkrb5} \ ${_kerberos5_lib_libroken} \ lib/libbz2 lib/libcom_err lib/libcrypt \ - lib/libexpat \ + lib/libelf lib/libexpat \ ${_lib_libgssapi} ${_lib_libipx} \ lib/libkiconv lib/libkvm lib/liblzma lib/libmd \ lib/ncurses/ncurses lib/ncurses/ncursesw \ diff --git a/lib/libprocstat/Makefile b/lib/libprocstat/Makefile index 98044834a..b1ac0ee96 100644 --- a/lib/libprocstat/Makefile +++ b/lib/libprocstat/Makefile @@ -6,6 +6,7 @@ LIB= procstat SRCS= cd9660.c \ common_kvm.c \ + core.c \ libprocstat.c \ msdosfs.c \ ntfs.c \ @@ -19,8 +20,8 @@ INCS= libprocstat.h CFLAGS+= -I. -I${.CURDIR} -D_KVM_VNODE SHLIB_MAJOR= 1 -DPADD= ${LIBKVM} ${LIBUTIL} -LDADD= -lkvm -lutil +DPADD= ${LIBELF} ${LIBKVM} ${LIBUTIL} +LDADD= -lelf -lkvm -lutil MAN= libprocstat.3 diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map index 050906636..085720cf4 100644 --- a/lib/libprocstat/Symbol.map +++ b/lib/libprocstat/Symbol.map @@ -16,5 +16,22 @@ FBSD_1.2 { }; FBSD_1.3 { + procstat_freeargv; + procstat_freeauxv; + procstat_freeenvv; + procstat_freegroups; + procstat_freekstack; + procstat_freevmmap; procstat_get_shm_info; + procstat_getargv; + procstat_getauxv; + procstat_getenvv; + procstat_getgroups; + procstat_getkstack; + procstat_getosrel; + procstat_getpathname; + procstat_getrlimit; + procstat_getumask; + procstat_getvmmap; + procstat_open_core; }; diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c new file mode 100644 index 000000000..70ab86df5 --- /dev/null +++ b/lib/libprocstat/core.c @@ -0,0 +1,433 @@ +/*- + * Copyright (c) 2013 Mikolaj Golub + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + +#define PROCSTAT_CORE_MAGIC 0x012DADB8 +struct procstat_core +{ + int pc_magic; + int pc_fd; + Elf *pc_elf; + GElf_Ehdr pc_ehdr; + GElf_Phdr pc_phdr; +}; + +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) +{ + struct procstat_core *core; + Elf *e; + GElf_Ehdr ehdr; + GElf_Phdr phdr; + size_t nph; + int fd, i; + + if (elf_version(EV_CURRENT) == EV_NONE) { + warnx("ELF library too old"); + return (NULL); + } + fd = open(filename, O_RDONLY, 0); + if (fd == -1) { + warn("open(%s)", filename); + return (NULL); + } + e = elf_begin(fd, ELF_C_READ, NULL); + if (e == NULL) { + warnx("elf_begin: %s", elf_errmsg(-1)); + goto fail; + } + if (elf_kind(e) != ELF_K_ELF) { + warnx("%s is not an ELF object", filename); + goto fail; + } + if (gelf_getehdr(e, &ehdr) == NULL) { + warnx("gelf_getehdr: %s", elf_errmsg(-1)); + goto fail; + } + if (ehdr.e_type != ET_CORE) { + warnx("%s is not a CORE file", filename); + goto fail; + } + if (elf_getphnum(e, &nph) == 0) { + warnx("program headers not found"); + goto fail; + } + for (i = 0; i < ehdr.e_phnum; i++) { + if (gelf_getphdr(e, i, &phdr) != &phdr) { + warnx("gelf_getphdr: %s", elf_errmsg(-1)); + goto fail; + } + if (phdr.p_type == PT_NOTE) + break; + } + if (i == ehdr.e_phnum) { + warnx("NOTE program header not found"); + goto fail; + } + core = malloc(sizeof(struct procstat_core)); + if (core == NULL) { + warn("malloc(%zu)", sizeof(struct procstat_core)); + goto fail; + } + core->pc_magic = PROCSTAT_CORE_MAGIC; + core->pc_fd = fd; + core->pc_elf = e; + core->pc_ehdr = ehdr; + core->pc_phdr = phdr; + + return (core); +fail: + if (e != NULL) + elf_end(e); + close(fd); + + return (NULL); +} + +void +procstat_core_close(struct procstat_core *core) +{ + + assert(core->pc_magic == PROCSTAT_CORE_MAGIC); + + elf_end(core->pc_elf); + close(core->pc_fd); + free(core); +} + +void * +procstat_core_get(struct procstat_core *core, enum psc_type type, void *buf, + size_t *lenp) +{ + Elf_Note nhdr; + off_t offset, eoffset; + vm_offset_t psstrings; + void *freebuf; + size_t len; + u_int32_t n_type; + int cstructsize, structsize; + char nbuf[8]; + + assert(core->pc_magic == PROCSTAT_CORE_MAGIC); + + switch(type) { + case PSC_TYPE_PROC: + n_type = NT_PROCSTAT_PROC; + structsize = sizeof(struct kinfo_proc); + break; + case PSC_TYPE_FILES: + n_type = NT_PROCSTAT_FILES; + structsize = sizeof(struct kinfo_file); + break; + case PSC_TYPE_VMMAP: + n_type = NT_PROCSTAT_VMMAP; + structsize = sizeof(struct kinfo_vmentry); + break; + case PSC_TYPE_GROUPS: + n_type = NT_PROCSTAT_GROUPS; + structsize = sizeof(gid_t); + break; + case PSC_TYPE_UMASK: + n_type = NT_PROCSTAT_UMASK; + structsize = sizeof(u_short); + break; + case PSC_TYPE_RLIMIT: + n_type = NT_PROCSTAT_RLIMIT; + structsize = sizeof(struct rlimit) * RLIM_NLIMITS; + break; + case PSC_TYPE_OSREL: + 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; + case PSC_TYPE_AUXV: + n_type = NT_PROCSTAT_AUXV; + structsize = sizeof(Elf_Auxinfo); + break; + default: + warnx("unknown core stat type: %d", type); + return (NULL); + } + + offset = core->pc_phdr.p_offset; + eoffset = offset + core->pc_phdr.p_filesz; + + while (offset < eoffset) { + if (!core_offset(core, offset)) + return (NULL); + if (!core_read(core, &nhdr, sizeof(nhdr))) + return (NULL); + + offset += sizeof(nhdr) + + roundup2(nhdr.n_namesz, sizeof(Elf32_Size)) + + roundup2(nhdr.n_descsz, sizeof(Elf32_Size)); + + if (nhdr.n_namesz == 0 && nhdr.n_descsz == 0) + break; + if (nhdr.n_type != n_type) + continue; + if (nhdr.n_namesz != 8) + continue; + if (!core_read(core, nbuf, sizeof(nbuf))) + return (NULL); + if (strcmp(nbuf, "FreeBSD") != 0) + continue; + if (nhdr.n_descsz < sizeof(cstructsize)) { + warnx("corrupted core file"); + return (NULL); + } + if (!core_read(core, &cstructsize, sizeof(cstructsize))) + return (NULL); + if (cstructsize != structsize) { + warnx("version mismatch"); + return (NULL); + } + len = nhdr.n_descsz - sizeof(cstructsize); + if (len == 0) + return (NULL); + if (buf != NULL) { + len = MIN(len, *lenp); + freebuf = NULL; + } else { + freebuf = buf = malloc(len); + if (buf == NULL) { + warn("malloc(%zu)", len); + return (NULL); + } + } + if (!core_read(core, buf, len)) { + 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); + } + + return (NULL); +} + +static bool +core_offset(struct procstat_core *core, off_t offset) +{ + + assert(core->pc_magic == PROCSTAT_CORE_MAGIC); + + if (lseek(core->pc_fd, offset, SEEK_SET) == -1) { + warn("core: lseek(%jd)", (intmax_t)offset); + return (false); + } + return (true); +} + +static bool +core_read(struct procstat_core *core, void *buf, size_t len) +{ + ssize_t n; + + assert(core->pc_magic == PROCSTAT_CORE_MAGIC); + + n = read(core->pc_fd, buf, len); + if (n == -1) { + warn("core: read"); + return (false); + } + if (n < (ssize_t)len) { + warnx("core: short read"); + return (false); + } + 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 new file mode 100644 index 000000000..6639abce1 --- /dev/null +++ b/lib/libprocstat/core.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2013 Mikolaj Golub + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _CORE_H +#define _CORE_H + +enum psc_type { + PSC_TYPE_PROC, + PSC_TYPE_FILES, + PSC_TYPE_VMMAP, + PSC_TYPE_GROUPS, + PSC_TYPE_UMASK, + PSC_TYPE_RLIMIT, + PSC_TYPE_OSREL, + PSC_TYPE_PSSTRINGS, + PSC_TYPE_ARGV, + PSC_TYPE_ENVV, + PSC_TYPE_AUXV, +}; + +struct procstat_core; + +void procstat_core_close(struct procstat_core *core); +void *procstat_core_get(struct procstat_core *core, enum psc_type type, + void * buf, size_t *lenp); +struct procstat_core *procstat_core_open(const char *filename); + +#endif /* !_CORE_H_ */ diff --git a/lib/libprocstat/libprocstat.3 b/lib/libprocstat/libprocstat.3 index dd163c214..3312427a0 100644 --- a/lib/libprocstat/libprocstat.3 +++ b/lib/libprocstat/libprocstat.3 @@ -24,17 +24,33 @@ .\" .\" $FreeBSD$ .\" -.Dd April 1, 2012 +.Dd April 20, 2013 .Dt LIBPROCSTAT 3 .Os .Sh NAME +.Nm procstat_open_core , .Nm procstat_open_kvm , .Nm procstat_open_sysctl , .Nm procstat_close , +.Nm procstat_getargv , +.Nm procstat_getauxv , +.Nm procstat_getenvv , .Nm procstat_getfiles , +.Nm procstat_getgroups , +.Nm procstat_getkstack , +.Nm procstat_getosrel , +.Nm procstat_getpathname , .Nm procstat_getprocs , +.Nm procstat_getumask , +.Nm procstat_getvmmap , +.Nm procstat_freeargv , +.Nm procstat_freeauxv , +.Nm procstat_freeenvv , .Nm procstat_freefiles , +.Nm procstat_freegroups , +.Nm procstat_freekstack , .Nm procstat_freeprocs , +.Nm procstat_freevmmap , .Nm procstat_get_pipe_info , .Nm procstat_get_pts_info , .Nm procstat_get_shm_info , @@ -50,12 +66,40 @@ .Ft void .Fn procstat_close "struct procstat *procstat" .Ft void +.Fo procstat_freeargv +.Fa "struct procstat *procstat" +.Fc +.Ft void +.Fo procstat_freeauxv +.Fa "struct procstat *procstat" +.Fa "Elf_Auxinfo *auxv" +.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" .Fc .Ft void +.Fo procstat_freegroups +.Fa "struct procstat *procstat" +.Fa "gid_t *groups" +.Fc +.Ft void +.Fo procstat_freekstack +.Fa "struct procstat *procstat" +.Fa "struct kinfo_kstack *kkstp" +.Fc +.Ft void .Fn procstat_freeprocs "struct procstat *procstat" "struct kinfo_proc *p" +.Ft void +.Fo procstat_freevmmap +.Fa "struct procstat *procstat" +.Fa "struct kinfo_vmentry *vmmap" +.Fc .Ft int .Fo procstat_get_pipe_info .Fa "struct procstat *procstat" @@ -91,12 +135,50 @@ .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 "Elf_Auxinfo *" +.Fo procstat_getauxv +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "unsigned int *count" +.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" .Fa "struct kinfo_proc *kp" .Fa "int mmapped" .Fc +.Ft "gid_t *" +.Fo procstat_getgroups +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "unsigned int *count" +.Fc +.Ft int +.Fo procstat_getosrel +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "int *osrelp" +.Fc +.Ft "struct kinfo_kstack *" +.Fo procstat_getkstack +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "unsigned int *count" +.Fc .Ft "struct kinfo_proc *" .Fo procstat_getprocs .Fa "struct procstat *procstat" @@ -104,6 +186,34 @@ .Fa "int arg" .Fa "unsigned int *count" .Fc +.Ft "int" +.Fo procstat_getpathname +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "char *pathname" +.Fa "size_t maxlen" +.Fc +.Ft "int" +.Fo procstat_getrlimit +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "int which" +.Fa "struct rlimit* rlimit" +.Fc +.Ft "int" +.Fo procstat_getumask +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "unsigned short *maskp" +.Fc +.Ft "struct kinfo_vmentry *" +.Fo procstat_getvmmap +.Fa "struct procstat *procstat" +.Fa "struct kinfo_proc *kp" +.Fa "unsigned int *count" +.Fc +.Ft "struct procstat *" +.Fn procstat_open_core "const char *filename" .Ft "struct procstat *" .Fn procstat_open_kvm "const char *nlistf" "const char *memf" .Ft "struct procstat *" @@ -116,7 +226,11 @@ retrieval from the running kernel via the .Xr sysctl 3 library backend, and for post-mortem analysis via the .Xr kvm 3 -library backend. +library backend, or from the process +.Xr core 5 +file, searching for statistics in special +.Xr elf 3 +note sections. .Pp The .Fn procstat_open_kvm @@ -129,6 +243,16 @@ or library routines, respectively, to access kernel state information used to retrieve processes and files states. The +.Fn procstat_open_core +uses +.Xr elf 3 +routines to access statistics stored as a set of notes in a process +.Xr core 5 +file, written by the kernel at the moment of the process abnormal termination. +The +.Fa filename +argument is the process core file name. +The .Fa nlistf argument is the executable image of the kernel being examined. If this argument is @@ -145,7 +269,7 @@ is assumed. See .Xr kvm_open 3 for more details. -Both functions dynamically allocate and return a +The functions dynamically allocate and return a .Vt procstat structure pointer used in the rest of the .Nm libprocstat @@ -179,6 +303,63 @@ 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_getauxv +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, and returns the auxiliary vector as a dynamically allocated array of +.Vt Elf_Auxinfo +elements. +The caller is responsible to free the allocated memory with a subsequent +.Fn procstat_freeauxv +function call. +.Pp +The .Fn procstat_getfiles function gets a pointer to the .Vt procstat @@ -197,6 +378,89 @@ The caller is responsible to free the allocated memory with a subsequent function call. .Pp The +.Fn procstat_getgroups +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, and returns the process groups as a dynamically allocated array of +.Vt gid_t +elements. +The caller is responsible to free the allocated memory with a subsequent +.Fn procstat_freegroups +function call. +.Pp +The +.Fn procstat_getkstack +function gets a pointer to the +.Vt procstat +structure initialized with one of the +.Fn procstat_open_* +functions, a pointer to +.Vt kinfo_proc +structure, and returns kernel stacks of the process as a dynamically allocated +array of +.Vt kinfo_kstack +structures. +The caller is responsible to free the allocated memory with a subsequent +.Fn procstat_freekstack +function call. +.Pp +The +.Fn procstat_getosrel +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, and returns osrel date in the 3rd reference parameter. +.Pp +The +.Fn procstat_getpathname +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, and copies the path of the process executable to +.Fa pathname +buffer, limiting to +.Fa maxlen +characters. +.Pp +The +.Fn procstat_getrlimit +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, resource index +.Fa which , +and returns the actual resource limit in the 4th reference parameter. +.Pp +The +.Fn procstat_getumask +function gets a pointer to the +.Vt procstat +structure, a pointer to +.Vt kinfo_proc +structure, and returns the process umask in the 3rd reference parameter. +.Pp +The +.Fn procstat_getvmmap +function gets a pointer to the +.Vt procstat +structure initialized with one of the +.Fn procstat_open_* +functions, a pointer to +.Vt kinfo_proc +structure, and returns VM layout of the process as a dynamically allocated +array of +.Vt kinfo_vmentry +structures. +The caller is responsible to free the allocated memory with a subsequent +.Fn procstat_freevmmap +function call. +.Pp +The .Fn procstat_get_pipe_info , .Fn procstat_get_pts_info , .Fn procstat_get_shm_info , @@ -250,10 +514,12 @@ argument indicates an actual error message in case of failure. .Xr pipe 2 , .Xr shm_open 2 , .Xr socket 2 , +.Xr elf 3 , .Xr kvm 3 , .Xr queue 3 , .Xr sysctl 3 , .Xr pts 4 , +.Xr core 5 , .Xr vnode 9 .Sh HISTORY The diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c index 11a817e09..b1f581c5b 100644 --- a/lib/libprocstat/libprocstat.c +++ b/lib/libprocstat/libprocstat.c @@ -36,7 +36,12 @@ __FBSDID("$FreeBSD$"); #include +#include #include +#include +#define _WANT_UCRED +#include +#undef _WANT_UCRED #include #include #include @@ -96,13 +101,22 @@ __FBSDID("$FreeBSD$"); #include #include "libprocstat_internal.h" #include "common_kvm.h" +#include "core.h" int statfs(const char *, struct statfs *); /* XXX */ #define PROCSTAT_KVM 1 #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); +static Elf_Auxinfo *procstat_getauxv_core(struct procstat_core *core, + unsigned int *cntp); +static Elf_Auxinfo *procstat_getauxv_sysctl(pid_t pid, unsigned int *cntp); static struct filestat_list *procstat_getfiles_kvm( struct procstat *procstat, struct kinfo_proc *kp, int mmapped); static struct filestat_list *procstat_getfiles_sysctl( @@ -128,6 +142,33 @@ static int procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst, struct vnstat *vn, char *errbuf); static int procstat_get_vnode_info_sysctl(struct filestat *fst, struct vnstat *vn, char *errbuf); +static gid_t *procstat_getgroups_core(struct procstat_core *core, + unsigned int *count); +static gid_t * procstat_getgroups_kvm(kvm_t *kd, struct kinfo_proc *kp, + unsigned int *count); +static gid_t *procstat_getgroups_sysctl(pid_t pid, unsigned int *count); +static struct kinfo_kstack *procstat_getkstack_sysctl(pid_t pid, + int *cntp); +static int procstat_getosrel_core(struct procstat_core *core, + int *osrelp); +static int procstat_getosrel_kvm(kvm_t *kd, struct kinfo_proc *kp, + int *osrelp); +static int procstat_getosrel_sysctl(pid_t pid, int *osrelp); +static int procstat_getpathname_core(struct procstat_core *core, + char *pathname, size_t maxlen); +static int procstat_getpathname_sysctl(pid_t pid, char *pathname, + size_t maxlen); +static int procstat_getrlimit_core(struct procstat_core *core, int which, + struct rlimit* rlimit); +static int procstat_getrlimit_kvm(kvm_t *kd, struct kinfo_proc *kp, + int which, struct rlimit* rlimit); +static int procstat_getrlimit_sysctl(pid_t pid, int which, + struct rlimit* rlimit); +static int procstat_getumask_core(struct procstat_core *core, + unsigned short *maskp); +static int procstat_getumask_kvm(kvm_t *kd, struct kinfo_proc *kp, + unsigned short *maskp); +static int procstat_getumask_sysctl(pid_t pid, unsigned short *maskp); static int vntype2psfsttype(int type); void @@ -137,6 +178,10 @@ procstat_close(struct procstat *procstat) assert(procstat); if (procstat->type == PROCSTAT_KVM) kvm_close(procstat->kd); + else if (procstat->type == PROCSTAT_CORE) + procstat_core_close(procstat->core); + procstat_freeargv(procstat); + procstat_freeenvv(procstat); free(procstat); } @@ -177,6 +222,27 @@ procstat_open_kvm(const char *nlistf, const char *memf) return (procstat); } +struct procstat * +procstat_open_core(const char *filename) +{ + struct procstat *procstat; + struct procstat_core *core; + + procstat = calloc(1, sizeof(*procstat)); + if (procstat == NULL) { + warn("malloc()"); + return (NULL); + } + core = procstat_core_open(filename); + if (core == NULL) { + free(procstat); + return (NULL); + } + procstat->type = PROCSTAT_CORE; + procstat->core = core; + return (procstat); +} + struct kinfo_proc * procstat_getprocs(struct procstat *procstat, int what, int arg, unsigned int *count) @@ -230,6 +296,15 @@ procstat_getprocs(struct procstat *procstat, int what, int arg, goto fail; } /* Perform simple consistency checks. */ + if ((len % sizeof(*p)) != 0 || p->ki_structsize != sizeof(*p)) { + warnx("kinfo_proc structure size mismatch (len = %zu)", len); + goto fail; + } + *count = len / sizeof(*p); + return (p); + } else if (procstat->type == PROCSTAT_CORE) { + p = procstat_core_get(procstat->core, PSC_TYPE_PROC, NULL, + &len); if ((len % sizeof(*p)) != 0 || p->ki_structsize != sizeof(*p)) { warnx("kinfo_proc structure size mismatch"); goto fail; @@ -258,13 +333,17 @@ procstat_freeprocs(struct procstat *procstat __unused, struct kinfo_proc *p) struct filestat_list * procstat_getfiles(struct procstat *procstat, struct kinfo_proc *kp, int mmapped) { - - if (procstat->type == PROCSTAT_SYSCTL) - return (procstat_getfiles_sysctl(procstat, kp, mmapped)); - else if (procstat->type == PROCSTAT_KVM) + + switch(procstat->type) { + case PROCSTAT_KVM: return (procstat_getfiles_kvm(procstat, kp, mmapped)); - else + case PROCSTAT_SYSCTL: + case PROCSTAT_CORE: + return (procstat_getfiles_sysctl(procstat, kp, mmapped)); + default: + warnx("unknown access method: %d", procstat->type); return (NULL); + } } void @@ -647,8 +726,62 @@ kinfo_uflags2fst(int fd) return (0); } +static struct kinfo_file * +kinfo_getfile_core(struct procstat_core *core, int *cntp) +{ + int cnt; + size_t len; + char *buf, *bp, *eb; + struct kinfo_file *kif, *kp, *kf; + + buf = procstat_core_get(core, PSC_TYPE_FILES, NULL, &len); + if (buf == NULL) + return (NULL); + /* + * XXXMG: The code below is just copy&past from libutil. + * The code duplication can be avoided if libutil + * is extended to provide something like: + * struct kinfo_file *kinfo_getfile_from_buf(const char *buf, + * size_t len, int *cntp); + */ + + /* Pass 1: count items */ + cnt = 0; + bp = buf; + eb = buf + len; + while (bp < eb) { + kf = (struct kinfo_file *)(uintptr_t)bp; + bp += kf->kf_structsize; + cnt++; + } + + kif = calloc(cnt, sizeof(*kif)); + if (kif == NULL) { + free(buf); + return (NULL); + } + bp = buf; + eb = buf + len; + kp = kif; + /* Pass 2: unpack */ + while (bp < eb) { + kf = (struct kinfo_file *)(uintptr_t)bp; + /* Copy/expand into pre-zeroed buffer */ + memcpy(kp, kf, kf->kf_structsize); + /* Advance to next packed record */ + bp += kf->kf_structsize; + /* Set field size to fixed length, advance */ + kp->kf_structsize = sizeof(*kp); + kp++; + } + free(buf); + *cntp = cnt; + return (kif); /* Caller must free() return value */ +} + static struct filestat_list * -procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, int mmapped) +procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, + int mmapped) { struct kinfo_file *kif, *files; struct kinfo_vmentry *kve, *vmentries; @@ -664,8 +797,16 @@ procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, int m assert(kp); if (kp->ki_fd == NULL) return (NULL); - - files = kinfo_getfile(kp->ki_pid, &cnt); + switch(procstat->type) { + case PROCSTAT_SYSCTL: + files = kinfo_getfile(kp->ki_pid, &cnt); + break; + case PROCSTAT_CORE: + files = kinfo_getfile_core(procstat->core, &cnt); + break; + default: + assert(!"invalid type"); + } if (files == NULL && errno != EPERM) { warn("kinfo_getfile()"); return (NULL); @@ -703,7 +844,7 @@ procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, int m STAILQ_INSERT_TAIL(head, entry, next); } if (mmapped != 0) { - vmentries = kinfo_getvmmap(kp->ki_pid, &cnt); + vmentries = procstat_getvmmap(procstat, kp, &cnt); procstat->vmentries = vmentries; if (vmentries == NULL || cnt == 0) goto fail; @@ -743,7 +884,8 @@ procstat_get_pipe_info(struct procstat *procstat, struct filestat *fst, if (procstat->type == PROCSTAT_KVM) { return (procstat_get_pipe_info_kvm(procstat->kd, fst, ps, errbuf)); - } else if (procstat->type == PROCSTAT_SYSCTL) { + } else if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) { return (procstat_get_pipe_info_sysctl(fst, ps, errbuf)); } else { warnx("unknown access method: %d", procstat->type); @@ -807,7 +949,8 @@ procstat_get_pts_info(struct procstat *procstat, struct filestat *fst, if (procstat->type == PROCSTAT_KVM) { return (procstat_get_pts_info_kvm(procstat->kd, fst, pts, errbuf)); - } else if (procstat->type == PROCSTAT_SYSCTL) { + } else if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) { return (procstat_get_pts_info_sysctl(fst, pts, errbuf)); } else { warnx("unknown access method: %d", procstat->type); @@ -869,7 +1012,8 @@ procstat_get_shm_info(struct procstat *procstat, struct filestat *fst, if (procstat->type == PROCSTAT_KVM) { return (procstat_get_shm_info_kvm(procstat->kd, fst, shm, errbuf)); - } else if (procstat->type == PROCSTAT_SYSCTL) { + } else if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) { return (procstat_get_shm_info_sysctl(fst, shm, errbuf)); } else { warnx("unknown access method: %d", procstat->type); @@ -949,7 +1093,8 @@ procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst, if (procstat->type == PROCSTAT_KVM) { return (procstat_get_vnode_info_kvm(procstat->kd, fst, vn, errbuf)); - } else if (procstat->type == PROCSTAT_SYSCTL) { + } else if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) { return (procstat_get_vnode_info_sysctl(fst, vn, errbuf)); } else { warnx("unknown access method: %d", procstat->type); @@ -1156,7 +1301,8 @@ procstat_get_socket_info(struct procstat *procstat, struct filestat *fst, if (procstat->type == PROCSTAT_KVM) { return (procstat_get_socket_info_kvm(procstat->kd, fst, sock, errbuf)); - } else if (procstat->type == PROCSTAT_SYSCTL) { + } else if (procstat->type == PROCSTAT_SYSCTL || + procstat->type == PROCSTAT_CORE) { return (procstat_get_socket_info_sysctl(fst, sock, errbuf)); } else { warnx("unknown access method: %d", procstat->type); @@ -1407,3 +1553,866 @@ getmnton(kvm_t *kd, struct mount *m) mhead = mt; 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) +{ + int cnt; + size_t len; + char *buf, *bp, *eb; + struct kinfo_vmentry *kiv, *kp, *kv; + + buf = procstat_core_get(core, PSC_TYPE_VMMAP, NULL, &len); + if (buf == NULL) + return (NULL); + + /* + * XXXMG: The code below is just copy&past from libutil. + * The code duplication can be avoided if libutil + * is extended to provide something like: + * struct kinfo_vmentry *kinfo_getvmmap_from_buf(const char *buf, + * size_t len, int *cntp); + */ + + /* Pass 1: count items */ + cnt = 0; + bp = buf; + eb = buf + len; + while (bp < eb) { + kv = (struct kinfo_vmentry *)(uintptr_t)bp; + bp += kv->kve_structsize; + cnt++; + } + + kiv = calloc(cnt, sizeof(*kiv)); + if (kiv == NULL) { + free(buf); + return (NULL); + } + bp = buf; + eb = buf + len; + kp = kiv; + /* Pass 2: unpack */ + while (bp < eb) { + kv = (struct kinfo_vmentry *)(uintptr_t)bp; + /* Copy/expand into pre-zeroed buffer */ + memcpy(kp, kv, kv->kve_structsize); + /* Advance to next packed record */ + bp += kv->kve_structsize; + /* Set field size to fixed length, advance */ + kp->kve_structsize = sizeof(*kp); + kp++; + } + free(buf); + *cntp = cnt; + return (kiv); /* Caller must free() return value */ +} + +struct kinfo_vmentry * +procstat_getvmmap(struct procstat *procstat, struct kinfo_proc *kp, + unsigned int *cntp) +{ + + switch(procstat->type) { + case PROCSTAT_KVM: + warnx("kvm method is not supported"); + return (NULL); + case PROCSTAT_SYSCTL: + return (kinfo_getvmmap(kp->ki_pid, cntp)); + case PROCSTAT_CORE: + return (kinfo_getvmmap_core(procstat->core, cntp)); + default: + warnx("unknown access method: %d", procstat->type); + return (NULL); + } +} + +void +procstat_freevmmap(struct procstat *procstat __unused, + struct kinfo_vmentry *vmmap) +{ + + free(vmmap); +} + +static gid_t * +procstat_getgroups_kvm(kvm_t *kd, struct kinfo_proc *kp, unsigned int *cntp) +{ + struct proc proc; + struct ucred ucred; + gid_t *groups; + size_t len; + + assert(kd != NULL); + assert(kp != NULL); + if (!kvm_read_all(kd, (unsigned long)kp->ki_paddr, &proc, + sizeof(proc))) { + warnx("can't read proc struct at %p for pid %d", + kp->ki_paddr, kp->ki_pid); + return (NULL); + } + if (proc.p_ucred == NOCRED) + return (NULL); + if (!kvm_read_all(kd, (unsigned long)proc.p_ucred, &ucred, + sizeof(ucred))) { + warnx("can't read ucred struct at %p for pid %d", + proc.p_ucred, kp->ki_pid); + return (NULL); + } + len = ucred.cr_ngroups * sizeof(gid_t); + groups = malloc(len); + if (groups == NULL) { + warn("malloc(%zu)", len); + return (NULL); + } + if (!kvm_read_all(kd, (unsigned long)ucred.cr_groups, groups, len)) { + warnx("can't read groups at %p for pid %d", + ucred.cr_groups, kp->ki_pid); + free(groups); + return (NULL); + } + *cntp = ucred.cr_ngroups; + return (groups); +} + +static gid_t * +procstat_getgroups_sysctl(pid_t pid, unsigned int *cntp) +{ + int mib[4]; + size_t len; + gid_t *groups; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_GROUPS; + mib[3] = pid; + len = (sysconf(_SC_NGROUPS_MAX) + 1) * sizeof(gid_t); + groups = malloc(len); + if (groups == NULL) { + warn("malloc(%zu)", len); + return (NULL); + } + if (sysctl(mib, 4, groups, &len, NULL, 0) == -1) { + warn("sysctl: kern.proc.groups: %d", pid); + free(groups); + return (NULL); + } + *cntp = len / sizeof(gid_t); + return (groups); +} + +static gid_t * +procstat_getgroups_core(struct procstat_core *core, unsigned int *cntp) +{ + size_t len; + gid_t *groups; + + groups = procstat_core_get(core, PSC_TYPE_GROUPS, NULL, &len); + if (groups == NULL) + return (NULL); + *cntp = len / sizeof(gid_t); + return (groups); +} + +gid_t * +procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp, + unsigned int *cntp) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + return (procstat_getgroups_kvm(procstat->kd, kp, cntp)); + case PROCSTAT_SYSCTL: + return (procstat_getgroups_sysctl(kp->ki_pid, cntp)); + case PROCSTAT_CORE: + return (procstat_getgroups_core(procstat->core, cntp)); + default: + warnx("unknown access method: %d", procstat->type); + return (NULL); + } +} + +void +procstat_freegroups(struct procstat *procstat __unused, gid_t *groups) +{ + + free(groups); +} + +static int +procstat_getumask_kvm(kvm_t *kd, struct kinfo_proc *kp, unsigned short *maskp) +{ + struct filedesc fd; + + assert(kd != NULL); + assert(kp != NULL); + if (kp->ki_fd == NULL) + return (-1); + if (!kvm_read_all(kd, (unsigned long)kp->ki_fd, &fd, sizeof(fd))) { + warnx("can't read filedesc at %p for pid %d", kp->ki_fd, + kp->ki_pid); + return (-1); + } + *maskp = fd.fd_cmask; + return (0); +} + +static int +procstat_getumask_sysctl(pid_t pid, unsigned short *maskp) +{ + int error; + int mib[4]; + size_t len; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_UMASK; + mib[3] = pid; + len = sizeof(*maskp); + error = sysctl(mib, 4, maskp, &len, NULL, 0); + if (error != 0 && errno != ESRCH) + warn("sysctl: kern.proc.umask: %d", pid); + return (error); +} + +static int +procstat_getumask_core(struct procstat_core *core, unsigned short *maskp) +{ + size_t len; + unsigned short *buf; + + buf = procstat_core_get(core, PSC_TYPE_UMASK, NULL, &len); + if (buf == NULL) + return (-1); + if (len < sizeof(*maskp)) { + free(buf); + return (-1); + } + *maskp = *buf; + free(buf); + return (0); +} + +int +procstat_getumask(struct procstat *procstat, struct kinfo_proc *kp, + unsigned short *maskp) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + return (procstat_getumask_kvm(procstat->kd, kp, maskp)); + case PROCSTAT_SYSCTL: + return (procstat_getumask_sysctl(kp->ki_pid, maskp)); + case PROCSTAT_CORE: + return (procstat_getumask_core(procstat->core, maskp)); + default: + warnx("unknown access method: %d", procstat->type); + return (-1); + } +} + +static int +procstat_getrlimit_kvm(kvm_t *kd, struct kinfo_proc *kp, int which, + struct rlimit* rlimit) +{ + struct proc proc; + unsigned long offset; + + assert(kd != NULL); + assert(kp != NULL); + assert(which >= 0 && which < RLIM_NLIMITS); + if (!kvm_read_all(kd, (unsigned long)kp->ki_paddr, &proc, + sizeof(proc))) { + warnx("can't read proc struct at %p for pid %d", + kp->ki_paddr, kp->ki_pid); + return (-1); + } + if (proc.p_limit == NULL) + return (-1); + offset = (unsigned long)proc.p_limit + sizeof(struct rlimit) * which; + if (!kvm_read_all(kd, offset, rlimit, sizeof(*rlimit))) { + warnx("can't read rlimit struct at %p for pid %d", + (void *)offset, kp->ki_pid); + return (-1); + } + return (0); +} + +static int +procstat_getrlimit_sysctl(pid_t pid, int which, struct rlimit* rlimit) +{ + int error, name[5]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_RLIMIT; + name[3] = pid; + name[4] = which; + len = sizeof(struct rlimit); + error = sysctl(name, 5, rlimit, &len, NULL, 0); + if (error < 0 && errno != ESRCH) { + warn("sysctl: kern.proc.rlimit: %d", pid); + return (-1); + } + if (error < 0 || len != sizeof(struct rlimit)) + return (-1); + return (0); +} + +static int +procstat_getrlimit_core(struct procstat_core *core, int which, + struct rlimit* rlimit) +{ + size_t len; + struct rlimit* rlimits; + + if (which < 0 || which >= RLIM_NLIMITS) { + errno = EINVAL; + warn("getrlimit: which"); + return (-1); + } + rlimits = procstat_core_get(core, PSC_TYPE_RLIMIT, NULL, &len); + if (rlimits == NULL) + return (-1); + if (len < sizeof(struct rlimit) * RLIM_NLIMITS) { + free(rlimits); + return (-1); + } + *rlimit = rlimits[which]; + return (0); +} + +int +procstat_getrlimit(struct procstat *procstat, struct kinfo_proc *kp, int which, + struct rlimit* rlimit) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + return (procstat_getrlimit_kvm(procstat->kd, kp, which, + rlimit)); + case PROCSTAT_SYSCTL: + return (procstat_getrlimit_sysctl(kp->ki_pid, which, rlimit)); + case PROCSTAT_CORE: + return (procstat_getrlimit_core(procstat->core, which, rlimit)); + default: + warnx("unknown access method: %d", procstat->type); + return (-1); + } +} + +static int +procstat_getpathname_sysctl(pid_t pid, char *pathname, size_t maxlen) +{ + int error, name[4]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_PATHNAME; + name[3] = pid; + len = maxlen; + error = sysctl(name, 4, pathname, &len, NULL, 0); + if (error != 0 && errno != ESRCH) + warn("sysctl: kern.proc.pathname: %d", pid); + if (len == 0) + pathname[0] = '\0'; + return (error); +} + +static int +procstat_getpathname_core(struct procstat_core *core, char *pathname, + size_t maxlen) +{ + struct kinfo_file *files; + int cnt, i, result; + + files = kinfo_getfile_core(core, &cnt); + if (files == NULL) + return (-1); + result = -1; + for (i = 0; i < cnt; i++) { + if (files[i].kf_fd != KF_FD_TYPE_TEXT) + continue; + strncpy(pathname, files[i].kf_path, maxlen); + result = 0; + break; + } + free(files); + return (result); +} + +int +procstat_getpathname(struct procstat *procstat, struct kinfo_proc *kp, + char *pathname, size_t maxlen) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + warnx("kvm method is not supported"); + return (-1); + case PROCSTAT_SYSCTL: + return (procstat_getpathname_sysctl(kp->ki_pid, pathname, + maxlen)); + case PROCSTAT_CORE: + return (procstat_getpathname_core(procstat->core, pathname, + maxlen)); + default: + warnx("unknown access method: %d", procstat->type); + return (-1); + } +} + +static int +procstat_getosrel_kvm(kvm_t *kd, struct kinfo_proc *kp, int *osrelp) +{ + struct proc proc; + + assert(kd != NULL); + assert(kp != NULL); + if (!kvm_read_all(kd, (unsigned long)kp->ki_paddr, &proc, + sizeof(proc))) { + warnx("can't read proc struct at %p for pid %d", + kp->ki_paddr, kp->ki_pid); + return (-1); + } + *osrelp = proc.p_osrel; + return (0); +} + +static int +procstat_getosrel_sysctl(pid_t pid, int *osrelp) +{ + int error, name[4]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_OSREL; + name[3] = pid; + len = sizeof(*osrelp); + error = sysctl(name, 4, osrelp, &len, NULL, 0); + if (error != 0 && errno != ESRCH) + warn("sysctl: kern.proc.osrel: %d", pid); + return (error); +} + +static int +procstat_getosrel_core(struct procstat_core *core, int *osrelp) +{ + size_t len; + int *buf; + + buf = procstat_core_get(core, PSC_TYPE_OSREL, NULL, &len); + if (buf == NULL) + return (-1); + if (len < sizeof(*osrelp)) { + free(buf); + return (-1); + } + *osrelp = *buf; + free(buf); + return (0); +} + +int +procstat_getosrel(struct procstat *procstat, struct kinfo_proc *kp, int *osrelp) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + return (procstat_getosrel_kvm(procstat->kd, kp, osrelp)); + case PROCSTAT_SYSCTL: + return (procstat_getosrel_sysctl(kp->ki_pid, osrelp)); + case PROCSTAT_CORE: + return (procstat_getosrel_core(procstat->core, osrelp)); + default: + warnx("unknown access method: %d", procstat->type); + return (-1); + } +} + +#define PROC_AUXV_MAX 256 + +#if __ELF_WORD_SIZE == 64 +static const char *elf32_sv_names[] = { + "Linux ELF32", + "FreeBSD ELF32", +}; + +static int +is_elf32_sysctl(pid_t pid) +{ + int error, name[4]; + size_t len, i; + static char sv_name[256]; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_SV_NAME; + name[3] = pid; + len = sizeof(sv_name); + error = sysctl(name, 4, sv_name, &len, NULL, 0); + if (error != 0 || len == 0) + return (0); + for (i = 0; i < sizeof(elf32_sv_names) / sizeof(*elf32_sv_names); i++) { + if (strncmp(sv_name, elf32_sv_names[i], sizeof(sv_name)) == 0) + return (1); + } + return (0); +} + +static Elf_Auxinfo * +procstat_getauxv32_sysctl(pid_t pid, unsigned int *cntp) +{ + Elf_Auxinfo *auxv; + Elf32_Auxinfo *auxv32; + void *ptr; + size_t len; + unsigned int i, count; + int name[4]; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_AUXV; + name[3] = pid; + len = PROC_AUXV_MAX * sizeof(Elf32_Auxinfo); + auxv = NULL; + auxv32 = malloc(len); + if (auxv32 == NULL) { + warn("malloc(%zu)", len); + goto out; + } + if (sysctl(name, 4, auxv32, &len, NULL, 0) == -1) { + if (errno != ESRCH && errno != EPERM) + warn("sysctl: kern.proc.auxv: %d: %d", pid, errno); + goto out; + } + count = len / sizeof(Elf_Auxinfo); + auxv = malloc(count * sizeof(Elf_Auxinfo)); + if (auxv == NULL) { + warn("malloc(%zu)", count * sizeof(Elf_Auxinfo)); + goto out; + } + for (i = 0; i < count; i++) { + /* + * XXX: We expect that values for a_type on a 32-bit platform + * are directly mapped to values on 64-bit one, which is not + * necessarily true. + */ + auxv[i].a_type = auxv32[i].a_type; + ptr = &auxv32[i].a_un; + auxv[i].a_un.a_val = *((uint32_t *)ptr); + } + *cntp = count; +out: + free(auxv32); + return (auxv); +} +#endif /* __ELF_WORD_SIZE == 64 */ + +static Elf_Auxinfo * +procstat_getauxv_sysctl(pid_t pid, unsigned int *cntp) +{ + Elf_Auxinfo *auxv; + int name[4]; + size_t len; + +#if __ELF_WORD_SIZE == 64 + if (is_elf32_sysctl(pid)) + return (procstat_getauxv32_sysctl(pid, cntp)); +#endif + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_AUXV; + name[3] = pid; + len = PROC_AUXV_MAX * sizeof(Elf_Auxinfo); + auxv = malloc(len); + if (auxv == NULL) { + warn("malloc(%zu)", len); + return (NULL); + } + if (sysctl(name, 4, auxv, &len, NULL, 0) == -1) { + if (errno != ESRCH && errno != EPERM) + warn("sysctl: kern.proc.auxv: %d: %d", pid, errno); + free(auxv); + return (NULL); + } + *cntp = len / sizeof(Elf_Auxinfo); + return (auxv); +} + +static Elf_Auxinfo * +procstat_getauxv_core(struct procstat_core *core, unsigned int *cntp) +{ + Elf_Auxinfo *auxv; + size_t len; + + auxv = procstat_core_get(core, PSC_TYPE_AUXV, NULL, &len); + if (auxv == NULL) + return (NULL); + *cntp = len / sizeof(Elf_Auxinfo); + return (auxv); +} + +Elf_Auxinfo * +procstat_getauxv(struct procstat *procstat, struct kinfo_proc *kp, + unsigned int *cntp) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + warnx("kvm method is not supported"); + return (NULL); + case PROCSTAT_SYSCTL: + return (procstat_getauxv_sysctl(kp->ki_pid, cntp)); + case PROCSTAT_CORE: + return (procstat_getauxv_core(procstat->core, cntp)); + default: + warnx("unknown access method: %d", procstat->type); + return (NULL); + } +} + +void +procstat_freeauxv(struct procstat *procstat __unused, Elf_Auxinfo *auxv) +{ + + free(auxv); +} + +static struct kinfo_kstack * +procstat_getkstack_sysctl(pid_t pid, int *cntp) +{ + struct kinfo_kstack *kkstp; + int error, name[4]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_KSTACK; + name[3] = pid; + + len = 0; + error = sysctl(name, 4, NULL, &len, NULL, 0); + if (error < 0 && errno != ESRCH && errno != EPERM && errno != ENOENT) { + warn("sysctl: kern.proc.kstack: %d", pid); + return (NULL); + } + if (error == -1 && errno == ENOENT) { + warnx("sysctl: kern.proc.kstack unavailable" + " (options DDB or options STACK required in kernel)"); + return (NULL); + } + if (error == -1) + return (NULL); + kkstp = malloc(len); + if (kkstp == NULL) { + warn("malloc(%zu)", len); + return (NULL); + } + if (sysctl(name, 4, kkstp, &len, NULL, 0) == -1) { + warn("sysctl: kern.proc.pid: %d", pid); + free(kkstp); + return (NULL); + } + *cntp = len / sizeof(*kkstp); + + return (kkstp); +} + +struct kinfo_kstack * +procstat_getkstack(struct procstat *procstat, struct kinfo_proc *kp, + unsigned int *cntp) +{ + switch(procstat->type) { + case PROCSTAT_KVM: + warnx("kvm method is not supported"); + return (NULL); + case PROCSTAT_SYSCTL: + return (procstat_getkstack_sysctl(kp->ki_pid, cntp)); + case PROCSTAT_CORE: + warnx("core method is not supported"); + return (NULL); + default: + warnx("unknown access method: %d", procstat->type); + return (NULL); + } +} + +void +procstat_freekstack(struct procstat *procstat __unused, + struct kinfo_kstack *kkstp) +{ + + free(kkstp); +} diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h index 662ea372a..6688cea77 100644 --- a/lib/libprocstat/libprocstat.h +++ b/lib/libprocstat/libprocstat.h @@ -29,6 +29,14 @@ #ifndef _LIBPROCSTAT_H_ #define _LIBPROCSTAT_H_ +/* + * XXX: sys/elf.h conflicts with zfs_context.h. Workaround this by not + * including conflicting parts when building zfs code. + */ +#ifndef ZFS +#include +#endif + /* * Vnode types. */ @@ -90,7 +98,10 @@ #define PS_FST_FFLAG_HASLOCK 0x4000 #define PS_FST_FFLAG_CAPABILITY 0x8000 +struct kinfo_kstack; +struct kinfo_vmentry; struct procstat; +struct rlimit; struct filestat { int fs_type; /* Descriptor type. */ int fs_flags; /* filestat specific flags. */ @@ -146,9 +157,19 @@ STAILQ_HEAD(filestat_list, filestat); __BEGIN_DECLS void procstat_close(struct procstat *procstat); +void procstat_freeargv(struct procstat *procstat); +#ifndef ZFS +void procstat_freeauxv(struct procstat *procstat, Elf_Auxinfo *auxv); +#endif +void procstat_freeenvv(struct procstat *procstat); +void procstat_freegroups(struct procstat *procstat, gid_t *groups); +void procstat_freekstack(struct procstat *procstat, + struct kinfo_kstack *kkstp); void procstat_freeprocs(struct procstat *procstat, struct kinfo_proc *p); void procstat_freefiles(struct procstat *procstat, struct filestat_list *head); +void procstat_freevmmap(struct procstat *procstat, + struct kinfo_vmentry *vmmap); struct filestat_list *procstat_getfiles(struct procstat *procstat, struct kinfo_proc *kp, int mmapped); struct kinfo_proc *procstat_getprocs(struct procstat *procstat, @@ -163,6 +184,29 @@ 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); +#ifndef ZFS +Elf_Auxinfo *procstat_getauxv(struct procstat *procstat, + struct kinfo_proc *kp, unsigned int *cntp); +#endif +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); +struct kinfo_kstack *procstat_getkstack(struct procstat *procstat, + struct kinfo_proc *kp, unsigned int *count); +int procstat_getosrel(struct procstat *procstat, struct kinfo_proc *kp, + int *osrelp); +int procstat_getpathname(struct procstat *procstat, struct kinfo_proc *kp, + char *pathname, size_t maxlen); +int procstat_getrlimit(struct procstat *procstat, struct kinfo_proc *kp, + int which, struct rlimit* rlimit); +int procstat_getumask(struct procstat *procstat, struct kinfo_proc *kp, + unsigned short* umask); +struct kinfo_vmentry *procstat_getvmmap(struct procstat *procstat, + struct kinfo_proc *kp, unsigned int *count); +struct procstat *procstat_open_core(const char *filename); struct procstat *procstat_open_sysctl(void); struct procstat *procstat_open_kvm(const char *nlistf, const char *memf); __END_DECLS diff --git a/lib/libprocstat/libprocstat_internal.h b/lib/libprocstat/libprocstat_internal.h index 1c1d84284..6ca3197cb 100644 --- a/lib/libprocstat/libprocstat_internal.h +++ b/lib/libprocstat/libprocstat_internal.h @@ -34,6 +34,9 @@ struct procstat { kvm_t *kd; void *vmentries; void *files; + void *argv; + void *envv; + struct procstat_core *core; }; #endif /* !_LIBPROCSTAT_INTERNAL_H_ */ -- 2.45.0