aboutsummaryrefslogtreecommitdiff
path: root/lib/libprocstat
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2013-09-05 20:18:59 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2013-09-05 20:18:59 +0000
commitd1d015864103b253b3fcb2f72a0da5b0cfeb31b6 (patch)
tree22b131dceb13c3df96da594fbaadb693504797c7 /lib/libprocstat
parent12d4083451fc39b3e831d4ea0bfa67d3b32cfb54 (diff)
parentb6f49c23a36f329cbf1e7f28078e17fd87f0e245 (diff)
downloadsrc-d1d015864103b253b3fcb2f72a0da5b0cfeb31b6.tar.gz
src-d1d015864103b253b3fcb2f72a0da5b0cfeb31b6.zip
Merge from head
Notes
Notes: svn path=/projects/bmake/; revision=255263
Diffstat (limited to 'lib/libprocstat')
-rw-r--r--lib/libprocstat/Makefile6
-rw-r--r--lib/libprocstat/Symbol.map18
-rw-r--r--lib/libprocstat/common_kvm.h1
-rw-r--r--lib/libprocstat/core.c433
-rw-r--r--lib/libprocstat/core.h53
-rw-r--r--lib/libprocstat/libprocstat.3286
-rw-r--r--lib/libprocstat/libprocstat.c1194
-rw-r--r--lib/libprocstat/libprocstat.h51
-rw-r--r--lib/libprocstat/libprocstat_internal.h3
9 files changed, 2003 insertions, 42 deletions
diff --git a/lib/libprocstat/Makefile b/lib/libprocstat/Makefile
index a29afc7a7d8d..af5a775d7d05 100644
--- a/lib/libprocstat/Makefile
+++ b/lib/libprocstat/Makefile
@@ -6,8 +6,10 @@ LIB= procstat
SRCS= cd9660.c \
common_kvm.c \
+ core.c \
libprocstat.c \
msdosfs.c \
+ smbfs.c \
udf.c
VERSION_DEF= ${.CURDIR}/Versions.def
@@ -17,8 +19,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 0509066369f0..1495bfc28c66 100644
--- a/lib/libprocstat/Symbol.map
+++ b/lib/libprocstat/Symbol.map
@@ -16,5 +16,23 @@ FBSD_1.2 {
};
FBSD_1.3 {
+ procstat_freeargv;
+ procstat_freeauxv;
+ procstat_freeenvv;
+ procstat_freegroups;
+ procstat_freekstack;
+ procstat_freevmmap;
+ procstat_get_sem_info;
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/common_kvm.h b/lib/libprocstat/common_kvm.h
index d5e08e1cfc09..06627bff448c 100644
--- a/lib/libprocstat/common_kvm.h
+++ b/lib/libprocstat/common_kvm.h
@@ -41,6 +41,7 @@ int devfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int isofs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int msdosfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int nfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
+int smbfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int udf_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int ufs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int zfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
diff --git a/lib/libprocstat/core.c b/lib/libprocstat/core.c
new file mode 100644
index 000000000000..70ab86df5b0b
--- /dev/null
+++ b/lib/libprocstat/core.c
@@ -0,0 +1,433 @@
+/*-
+ * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/elf.h>
+#include <sys/exec.h>
+#include <sys/user.h>
+
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libelf.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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 000000000000..6639abce174d
--- /dev/null
+++ b/lib/libprocstat/core.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
+ * 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 dd163c214cd0..b472900dd8b9 100644
--- a/lib/libprocstat/libprocstat.3
+++ b/lib/libprocstat/libprocstat.3
@@ -24,19 +24,36 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 1, 2012
+.Dd May 3, 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_sem_info ,
.Nm procstat_get_shm_info ,
.Nm procstat_get_socket_info ,
.Nm procstat_get_vnode_info
@@ -50,12 +67,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"
@@ -71,6 +116,13 @@
.Fa "char *errbuf"
.Fc
.Ft int
+.Fo procstat_get_sem_info
+.Fa "struct procstat *procstat"
+.Fa "struct filestat *fst"
+.Fa "struct semstat *sem"
+.Fa "char *errbuf"
+.Fc
+.Ft int
.Fo procstat_get_shm_info
.Fa "struct procstat *procstat"
.Fa "struct filestat *fst"
@@ -91,12 +143,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 +194,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 +234,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 +251,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 +277,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 +311,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,14 +386,98 @@ 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_sem_info ,
.Fn procstat_get_shm_info ,
.Fn procstat_get_socket_info
and
.Fn procstat_get_vnode_info
functions are used to retrieve information about pipes, pseudo-terminals,
-shared memory objects,
+semaphores, shared memory objects,
sockets, and vnodes, respectively.
Each of them have a similar interface API.
The
@@ -241,6 +514,8 @@ argument indicates an actual error message in case of failure.
.Nm procstat_get_pipe_info
.It Li PS_FST_TYPE_PTS
.Nm procstat_get_pts_info
+.It Li PS_FST_TYPE_SEM
+.Nm procstat_get_sem_info
.It Li PS_FST_TYPE_SHM
.Nm procstat_get_shm_info
.El
@@ -250,10 +525,13 @@ 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 sem_open 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 f23ec96dfa2f..9056151e2b4d 100644
--- a/lib/libprocstat/libprocstat.c
+++ b/lib/libprocstat/libprocstat.c
@@ -36,7 +36,12 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/elf.h>
#include <sys/time.h>
+#include <sys/resourcevar.h>
+#define _WANT_UCRED
+#include <sys/ucred.h>
+#undef _WANT_UCRED
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/stat.h>
@@ -54,6 +59,7 @@ __FBSDID("$FreeBSD$");
#define _WANT_FILE
#include <sys/file.h>
#include <sys/conf.h>
+#include <sys/ksem.h>
#include <sys/mman.h>
#define _KERNEL
#include <sys/mount.h>
@@ -96,13 +102,22 @@ __FBSDID("$FreeBSD$");
#include <libprocstat.h>
#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(
@@ -115,6 +130,10 @@ static int procstat_get_pts_info_sysctl(struct filestat *fst,
struct ptsstat *pts, char *errbuf);
static int procstat_get_pts_info_kvm(kvm_t *kd, struct filestat *fst,
struct ptsstat *pts, char *errbuf);
+static int procstat_get_sem_info_sysctl(struct filestat *fst,
+ struct semstat *sem, char *errbuf);
+static int procstat_get_sem_info_kvm(kvm_t *kd, struct filestat *fst,
+ struct semstat *sem, char *errbuf);
static int procstat_get_shm_info_sysctl(struct filestat *fst,
struct shmstat *shm, char *errbuf);
static int procstat_get_shm_info_kvm(kvm_t *kd, struct filestat *fst,
@@ -128,6 +147,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 +183,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,12 +227,33 @@ 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)
{
struct kinfo_proc *p0, *p;
- size_t len;
+ size_t len, olen;
int name[4];
int cnt;
int error;
@@ -219,18 +290,31 @@ procstat_getprocs(struct procstat *procstat, int what, int arg,
warnx("no processes?");
goto fail;
}
- p = malloc(len);
- if (p == NULL) {
- warnx("malloc(%zu)", len);
- goto fail;
- }
- error = sysctl(name, 4, p, &len, NULL, 0);
+ do {
+ len += len / 10;
+ p = reallocf(p, len);
+ if (p == NULL) {
+ warnx("reallocf(%zu)", len);
+ goto fail;
+ }
+ olen = len;
+ error = sysctl(name, 4, p, &len, NULL, 0);
+ } while (error < 0 && errno == ENOMEM && olen == len);
if (error < 0 && errno != EPERM) {
warn("sysctl(kern.proc)");
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 +342,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
@@ -290,7 +378,7 @@ procstat_freefiles(struct procstat *procstat, struct filestat_list *head)
static struct filestat *
filestat_new_entry(void *typedep, int type, int fd, int fflags, int uflags,
- int refcount, off_t offset, char *path, cap_rights_t cap_rights)
+ int refcount, off_t offset, char *path, cap_rights_t *cap_rightsp)
{
struct filestat *entry;
@@ -307,7 +395,7 @@ filestat_new_entry(void *typedep, int type, int fd, int fflags, int uflags,
entry->fs_ref_count = refcount;
entry->fs_offset = offset;
entry->fs_path = path;
- entry->fs_cap_rights = cap_rights;
+ entry->fs_cap_rights = *cap_rightsp;
return (entry);
}
@@ -477,6 +565,10 @@ procstat_getfiles_kvm(struct procstat *procstat, struct kinfo_proc *kp, int mmap
data = file.f_data;
break;
#endif
+ case DTYPE_SEM:
+ type = PS_FST_TYPE_SEM;
+ data = file.f_data;
+ break;
case DTYPE_SHM:
type = PS_FST_TYPE_SHM;
data = file.f_data;
@@ -646,8 +738,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;
@@ -663,8 +809,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);
@@ -697,12 +851,12 @@ procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, int m
* Create filestat entry.
*/
entry = filestat_new_entry(kif, type, fd, fflags, uflags,
- refcount, offset, path, cap_rights);
+ refcount, offset, path, &cap_rights);
if (entry != NULL)
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;
@@ -742,11 +896,13 @@ 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);
- snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
return (1);
}
}
@@ -775,7 +931,8 @@ procstat_get_pipe_info_kvm(kvm_t *kd, struct filestat *fst,
return (0);
fail:
- snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
return (1);
}
@@ -806,11 +963,13 @@ 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);
- snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
return (1);
}
}
@@ -838,7 +997,8 @@ procstat_get_pts_info_kvm(kvm_t *kd, struct filestat *fst,
return (0);
fail:
- snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
return (1);
}
@@ -860,6 +1020,89 @@ procstat_get_pts_info_sysctl(struct filestat *fst, struct ptsstat *pts,
}
int
+procstat_get_sem_info(struct procstat *procstat, struct filestat *fst,
+ struct semstat *sem, char *errbuf)
+{
+
+ assert(sem);
+ if (procstat->type == PROCSTAT_KVM) {
+ return (procstat_get_sem_info_kvm(procstat->kd, fst, sem,
+ errbuf));
+ } else if (procstat->type == PROCSTAT_SYSCTL ||
+ procstat->type == PROCSTAT_CORE) {
+ return (procstat_get_sem_info_sysctl(fst, sem, errbuf));
+ } else {
+ warnx("unknown access method: %d", procstat->type);
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ return (1);
+ }
+}
+
+static int
+procstat_get_sem_info_kvm(kvm_t *kd, struct filestat *fst,
+ struct semstat *sem, char *errbuf)
+{
+ struct ksem ksem;
+ void *ksemp;
+ char *path;
+ int i;
+
+ assert(kd);
+ assert(sem);
+ assert(fst);
+ bzero(sem, sizeof(*sem));
+ ksemp = fst->fs_typedep;
+ if (ksemp == NULL)
+ goto fail;
+ if (!kvm_read_all(kd, (unsigned long)ksemp, &ksem,
+ sizeof(struct ksem))) {
+ warnx("can't read ksem at %p", (void *)ksemp);
+ goto fail;
+ }
+ sem->mode = S_IFREG | ksem.ks_mode;
+ sem->value = ksem.ks_value;
+ if (fst->fs_path == NULL && ksem.ks_path != NULL) {
+ path = malloc(MAXPATHLEN);
+ for (i = 0; i < MAXPATHLEN - 1; i++) {
+ if (!kvm_read_all(kd, (unsigned long)ksem.ks_path + i,
+ path + i, 1))
+ break;
+ if (path[i] == '\0')
+ break;
+ }
+ path[i] = '\0';
+ if (i == 0)
+ free(path);
+ else
+ fst->fs_path = path;
+ }
+ return (0);
+
+fail:
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ return (1);
+}
+
+static int
+procstat_get_sem_info_sysctl(struct filestat *fst, struct semstat *sem,
+ char *errbuf __unused)
+{
+ struct kinfo_file *kif;
+
+ assert(sem);
+ assert(fst);
+ bzero(sem, sizeof(*sem));
+ kif = fst->fs_typedep;
+ if (kif == NULL)
+ return (0);
+ sem->value = kif->kf_un.kf_sem.kf_sem_value;
+ sem->mode = kif->kf_un.kf_sem.kf_sem_mode;
+ return (0);
+}
+
+int
procstat_get_shm_info(struct procstat *procstat, struct filestat *fst,
struct shmstat *shm, char *errbuf)
{
@@ -868,11 +1111,13 @@ 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);
- snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
return (1);
}
}
@@ -918,7 +1163,8 @@ procstat_get_shm_info_kvm(kvm_t *kd, struct filestat *fst,
return (0);
fail:
- snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
return (1);
}
@@ -948,11 +1194,13 @@ 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);
- snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
return (1);
}
}
@@ -972,6 +1220,7 @@ procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst,
FSTYPE(isofs),
FSTYPE(msdosfs),
FSTYPE(nfs),
+ FSTYPE(smbfs),
FSTYPE(udf),
FSTYPE(ufs),
#ifdef LIBPROCSTAT_ZFS
@@ -1019,7 +1268,8 @@ procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst,
break;
}
if (i == NTYPES) {
- snprintf(errbuf, _POSIX2_LINE_MAX, "?(%s)", tagstr);
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "?(%s)", tagstr);
return (1);
}
vn->vn_mntdir = getmnton(kd, vnode.v_mount);
@@ -1033,7 +1283,8 @@ procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst,
return (0);
fail:
- snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
return (1);
}
@@ -1114,7 +1365,10 @@ procstat_get_vnode_info_sysctl(struct filestat *fst, struct vnstat *vn,
if (vntype == PS_FST_VTYPE_VNON || vntype == PS_FST_VTYPE_VBAD)
return (0);
if ((status & KF_ATTR_VALID) == 0) {
- snprintf(errbuf, _POSIX2_LINE_MAX, "? (no info available)");
+ if (errbuf != NULL) {
+ snprintf(errbuf, _POSIX2_LINE_MAX,
+ "? (no info available)");
+ }
return (1);
}
if (path && *path) {
@@ -1150,11 +1404,13 @@ 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);
- snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
return (1);
}
}
@@ -1252,7 +1508,8 @@ procstat_get_socket_info_kvm(kvm_t *kd, struct filestat *fst,
return (0);
fail:
- snprintf(errbuf, _POSIX2_LINE_MAX, "error");
+ if (errbuf != NULL)
+ snprintf(errbuf, _POSIX2_LINE_MAX, "error");
return (1);
}
@@ -1401,3 +1658,868 @@ 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:
+ /* XXX: Return empty string. */
+ if (maxlen > 0)
+ pathname[0] = '\0';
+ return (0);
+ 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 1c55aa756200..7af6ccba14a2 100644
--- a/lib/libprocstat/libprocstat.h
+++ b/lib/libprocstat/libprocstat.h
@@ -30,6 +30,15 @@
#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 <sys/elf.h>
+#endif
+#include <sys/caprights.h>
+
+/*
* Vnode types.
*/
#define PS_FST_VTYPE_VNON 1
@@ -89,7 +98,10 @@
#define PS_FST_FFLAG_EXEC 0x2000
#define PS_FST_FFLAG_HASLOCK 0x4000
+struct kinfo_kstack;
+struct kinfo_vmentry;
struct procstat;
+struct rlimit;
struct filestat {
int fs_type; /* Descriptor type. */
int fs_flags; /* filestat specific flags. */
@@ -122,6 +134,10 @@ struct pipestat {
uint64_t addr;
uint64_t peer;
};
+struct semstat {
+ uint32_t value;
+ uint16_t mode;
+};
struct shmstat {
uint64_t size;
uint16_t mode;
@@ -145,9 +161,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,
@@ -156,12 +182,37 @@ int procstat_get_pipe_info(struct procstat *procstat, struct filestat *fst,
struct pipestat *pipe, char *errbuf);
int procstat_get_pts_info(struct procstat *procstat, struct filestat *fst,
struct ptsstat *pts, char *errbuf);
+int procstat_get_sem_info(struct procstat *procstat, struct filestat *fst,
+ struct semstat *sem, char *errbuf);
int procstat_get_shm_info(struct procstat *procstat, struct filestat *fst,
struct shmstat *shm, char *errbuf);
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 1c1d84284701..6ca3197cbc4e 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_ */