summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2009-06-07 03:09:35 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2009-06-07 03:09:35 +0000
commit015c62b69daf0ef2f78e7e50cded1b5eae40ee94 (patch)
treed0175d8b91c8bd05a0e7d763d83881d134f27273
parent35f0486b79701fae79f4e1da115887021cca6abf (diff)
Add kvm_getfile2 which uses KERN_FILE2 when possible, falling
back on kmem grovelling for non-live kernels. OK deraadt@
-rw-r--r--include/kvm.h4
-rw-r--r--lib/libkvm/Makefile5
-rw-r--r--lib/libkvm/kvm.c7
-rw-r--r--lib/libkvm/kvm_file2.c839
-rw-r--r--lib/libkvm/kvm_getfiles.360
-rw-r--r--lib/libkvm/kvm_private.h3
-rw-r--r--lib/libkvm/shlib_version2
7 files changed, 911 insertions, 9 deletions
diff --git a/include/kvm.h b/include/kvm.h
index 027ef420543..5366024f016 100644
--- a/include/kvm.h
+++ b/include/kvm.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: kvm.h,v 1.13 2006/03/31 03:09:16 deraadt Exp $ */
+/* $OpenBSD: kvm.h,v 1.14 2009/06/07 03:09:34 millert Exp $ */
/* $NetBSD: kvm.h,v 1.7 1996/04/19 12:02:50 leo Exp $ */
/*-
@@ -66,6 +66,8 @@ char **kvm_getenvv2(kvm_t *, const struct kinfo_proc2 *, int);
char *kvm_geterr(kvm_t *);
int kvm_getloadavg(kvm_t *, double [], int);
char *kvm_getfiles(kvm_t *, int, int, int *);
+struct kinfo_file2 *
+ kvm_getfile2(kvm_t *, int, int, size_t, int *);
struct kinfo_proc *
kvm_getprocs(kvm_t *, int, int, int *);
struct kinfo_proc2 *
diff --git a/lib/libkvm/Makefile b/lib/libkvm/Makefile
index b7a658c0407..1871a9461b6 100644
--- a/lib/libkvm/Makefile
+++ b/lib/libkvm/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.8 2005/11/24 20:49:18 deraadt Exp $
+# $OpenBSD: Makefile,v 1.9 2009/06/07 03:09:34 millert Exp $
# $NetBSD: Makefile,v 1.11 1996/03/18 22:33:07 thorpej Exp $
# from: @(#)Makefile 8.1 (Berkeley) 6/4/93
@@ -15,7 +15,7 @@ CFLAGS+=-D${MACHINE}
SRCS= kvm_${MACHINE_ARCH}.c
.endif
-SRCS+= kvm.c kvm_file.c kvm_getloadavg.c kvm_proc.c
+SRCS+= kvm.c kvm_file.c kvm_file2.c kvm_getloadavg.c kvm_proc.c
MAN= kvm.3 kvm_dump.3 kvm_geterr.3 kvm_getfiles.3 kvm_getloadavg.3 \
kvm_getprocs.3 kvm_nlist.3 kvm_open.3 kvm_read.3
@@ -23,6 +23,7 @@ MAN= kvm.3 kvm_dump.3 kvm_geterr.3 kvm_getfiles.3 kvm_getloadavg.3 \
MLINKS+=kvm_getprocs.3 kvm_getargv.3 kvm_getprocs.3 kvm_getenvv.3
MLINKS+=kvm_getprocs.3 kvm_getproc2.3
MLINKS+=kvm_getprocs.3 kvm_getargv2.3 kvm_getprocs.3 kvm_getenvv2.3
+MLINKS+=kvm_getfiles.3 kvm_getfile2.3
MLINKS+=kvm_open.3 kvm_openfiles.3 kvm_open.3 kvm_close.3
MLINKS+=kvm_read.3 kvm_write.3
MLINKS+=kvm_dump.3 kvm_dump_mkheader.3 kvm_dump.3 kvm_dump_wrtheader.3
diff --git a/lib/libkvm/kvm.c b/lib/libkvm/kvm.c
index c8b5fd333b4..ca04ff4d731 100644
--- a/lib/libkvm/kvm.c
+++ b/lib/libkvm/kvm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kvm.c,v 1.46 2009/01/21 22:18:00 miod Exp $ */
+/* $OpenBSD: kvm.c,v 1.47 2009/06/07 03:09:34 millert Exp $ */
/* $NetBSD: kvm.c,v 1.43 1996/05/05 04:31:59 gwr Exp $ */
/*-
@@ -38,7 +38,7 @@
#if 0
static char sccsid[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94";
#else
-static char *rcsid = "$OpenBSD: kvm.c,v 1.46 2009/01/21 22:18:00 miod Exp $";
+static char *rcsid = "$OpenBSD: kvm.c,v 1.47 2009/06/07 03:09:34 millert Exp $";
#endif
#endif /* LIBC_SCCS and not lint */
@@ -184,6 +184,7 @@ _kvm_open(kvm_t *kd, const char *uf, const char *mf, const char *sf,
kd->swfd = -1;
kd->nlfd = -1;
kd->alive = 0;
+ kd->filebase = 0;
kd->procbase = 0;
kd->procbase2 = 0;
kd->nbpg = getpagesize();
@@ -641,6 +642,8 @@ kvm_close(kvm_t *kd)
free((void *)kd->cpu_data);
if (kd->kcore_hdr != NULL)
free((void *)kd->kcore_hdr);
+ if (kd->filebase != 0)
+ free((void *)kd->filebase);
if (kd->procbase != 0)
free((void *)kd->procbase);
if (kd->procbase2 != 0)
diff --git a/lib/libkvm/kvm_file2.c b/lib/libkvm/kvm_file2.c
new file mode 100644
index 00000000000..0d92abddcc6
--- /dev/null
+++ b/lib/libkvm/kvm_file2.c
@@ -0,0 +1,839 @@
+/* $OpenBSD: kvm_file2.c,v 1.1 2009/06/07 03:09:34 millert Exp $ */
+
+/*
+ * Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * Copyright (c) 1989, 1992, 1993
+ * The Regents of the University of California. 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: kvm_file2.c,v 1.1 2009/06/07 03:09:34 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Extended file list interface for kvm. pstat, fstat and netstat are
+ * users of this code, so we've factored it out into a separate module.
+ * Thus, we keep this grunge out of the other kvm applications (i.e.,
+ * most other applications are interested only in open/close/read/nlist).
+ */
+
+#include <sys/param.h>
+#define _KERNEL
+#include <machine/cpu.h>
+#include <sys/ucred.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+#include <dev/systrace.h>
+#undef _KERNEL
+#include <sys/vnode.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/event.h>
+#include <sys/eventvar.h>
+#include <sys/unpcb.h>
+#include <sys/filedesc.h>
+#include <sys/pipe.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <isofs/cd9660/iso.h>
+#include <isofs/cd9660/cd9660_extern.h>
+#include <isofs/cd9660/cd9660_node.h>
+
+#define _KERNEL
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#undef _KERNEL
+
+#include <nfs/nfsproto.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+
+#include <nnpfs/nnpfs_config.h>
+#include <nnpfs/nnpfs_node.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif
+
+#include <nlist.h>
+#include <kvm.h>
+#include <db.h>
+
+#include "kvm_private.h"
+
+#define KREAD(kd, addr, obj) \
+ (kvm_read(kd, addr, obj, sizeof(*obj)) != sizeof(*obj))
+
+static struct kinfo_file2 *kvm_deadfile2_byfile(kvm_t *, int, int,
+ size_t, int *);
+static struct kinfo_file2 *kvm_deadfile2_byid(kvm_t *, int, int,
+ size_t, int *);
+static int fill_file2(kvm_t *, struct kinfo_file2 *, struct file *,
+ struct vnode *, struct proc *, int);
+static int filestat(kvm_t *, struct kinfo_file2 *, struct vnode *);
+
+struct kinfo_file2 *
+kvm_getfile2(kvm_t *kd, int op, int arg, size_t esize, int *cnt)
+{
+ int mib[6], rv;
+ size_t size;
+
+ if (kd->filebase != NULL) {
+ free(kd->filebase);
+ /*
+ * Clear this pointer in case this call fails. Otherwise,
+ * kvm_close() will free it again.
+ */
+ kd->filebase = 0;
+ }
+
+ if (ISALIVE(kd)) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_FILE2;
+ mib[2] = op;
+ mib[3] = arg;
+ mib[4] = esize;
+ mib[5] = 0;
+
+ /* find size and alloc buffer */
+ rv = sysctl(mib, 6, NULL, &size, NULL, 0);
+ if (rv == -1) {
+ if (kd->vmfd != -1)
+ goto deadway;
+ _kvm_syserr(kd, kd->program, "kvm_getfiles");
+ return (NULL);
+ }
+ kd->filebase = _kvm_malloc(kd, size);
+ if (kd->filebase == NULL)
+ return (NULL);
+
+ /* get actual data */
+ mib[5] = size / esize;
+ rv = sysctl(mib, 6, kd->filebase, &size, NULL, 0);
+ if (rv == -1) {
+ _kvm_syserr(kd, kd->program, "kvm_getfiles");
+ return (NULL);
+ }
+ *cnt = size / esize;
+ return ((struct kinfo_file2 *)kd->filebase);
+ } else {
+ deadway:
+ switch (op) {
+ case KERN_FILE_BYFILE:
+ if (arg != 0) {
+ _kvm_err(kd, kd->program,
+ "%s: invalid argument");
+ return (NULL);
+ }
+ return (kvm_deadfile2_byfile(kd, op, arg, esize, cnt));
+ break;
+ case KERN_FILE_BYPID:
+ case KERN_FILE_BYUID:
+ return (kvm_deadfile2_byid(kd, op, arg, esize, cnt));
+ break;
+ default:
+ return (NULL);
+ }
+ }
+}
+
+static struct kinfo_file2 *
+kvm_deadfile2_byfile(kvm_t *kd, int op, int arg, size_t esize, int *cnt)
+{
+ size_t size;
+ struct nlist nl[3], *p;
+ int buflen = kd->arglen, n = 0;
+ char *where = kd->argspc;
+ struct kinfo_file2 *kf;
+ struct file *fp, file;
+ struct filelist filehead;
+ int nfiles;
+
+ nl[0].n_name = "_filehead";
+ nl[1].n_name = "_nfiles";
+ nl[2].n_name = 0;
+
+ if (kvm_nlist(kd, nl) != 0) {
+ for (p = nl; p->n_type != 0; ++p)
+ ;
+ _kvm_err(kd, kd->program,
+ "%s: no such symbol", p->n_name);
+ return (NULL);
+ }
+ if (KREAD(kd, nl[0].n_value, &filehead)) {
+ _kvm_err(kd, kd->program, "can't read filehead");
+ return (NULL);
+ }
+ if (KREAD(kd, nl[1].n_value, &nfiles)) {
+ _kvm_err(kd, kd->program, "can't read nfiles");
+ return (NULL);
+ }
+ size = (nfiles + 10) * sizeof(struct kinfo_file2);
+ kd->filebase = _kvm_malloc(kd, size);
+ if (kd->filebase == NULL)
+ return (NULL);
+
+ LIST_FOREACH(fp, &filehead, f_list) {
+ if (buflen < sizeof(struct kinfo_file2))
+ break;
+
+ if (KREAD(kd, (long)fp, &file)) {
+ _kvm_err(kd, kd->program, "can't read kfp");
+ return (NULL);
+ }
+ kf = (struct kinfo_file2 *)where;
+ where += sizeof(struct kinfo_file2);
+ buflen -= sizeof(struct kinfo_file2);
+ n++;
+ if (fill_file2(kd, kf, fp, NULL, NULL, 0) == -1)
+ return (NULL);
+ }
+ if (n != nfiles) {
+ _kvm_err(kd, kd->program, "inconsistent nfiles");
+ return (NULL);
+ }
+ *cnt = n;
+ return (kf);
+}
+
+static struct kinfo_file2 *
+kvm_deadfile2_byid(kvm_t *kd, int op, int arg, size_t esize, int *cnt)
+{
+ size_t size;
+ struct nlist nl[5], *np;
+ int buflen = kd->arglen, n = 0;
+ char *where = kd->argspc;
+ struct kinfo_file2 *kf;
+ struct file *fp, file;
+ struct filelist filehead;
+ struct filedesc0 filed0;
+#define filed filed0.fd_fd
+ struct proclist allproc;
+ struct proc *p, proc;
+ struct process process;
+ struct pcred pcred;
+ struct ucred ucred;
+ int i, nfiles, nprocs;
+
+ nl[0].n_name = "_filehead";
+ nl[1].n_name = "_nfiles";
+ nl[2].n_name = "_nprocs";
+ nl[3].n_name = "_allproc";
+ nl[4].n_name = 0;
+
+ if (kvm_nlist(kd, nl) != 0) {
+ for (np = nl; np->n_type != 0; ++np)
+ ;
+ _kvm_err(kd, kd->program,
+ "%s: no such symbol", np->n_name);
+ return (NULL);
+ }
+ if (KREAD(kd, nl[0].n_value, &filehead)) {
+ _kvm_err(kd, kd->program, "can't read filehead");
+ return (NULL);
+ }
+ if (KREAD(kd, nl[1].n_value, &nfiles)) {
+ _kvm_err(kd, kd->program, "can't read nfiles");
+ return (NULL);
+ }
+ if (KREAD(kd, nl[2].n_value, &nprocs)) {
+ _kvm_err(kd, kd->program, "can't read nprocs");
+ return (NULL);
+ }
+ if (KREAD(kd, nl[3].n_value, &allproc)) {
+ _kvm_err(kd, kd->program, "can't read allproc");
+ return (NULL);
+ }
+ /* this may be more room than we need but counting is expensive */
+ size = (nfiles + 10) * sizeof(struct kinfo_file2);
+ kd->filebase = _kvm_malloc(kd, size);
+ if (kd->filebase == NULL)
+ return (NULL);
+
+ LIST_FOREACH(p, &allproc, p_list) {
+ if (buflen < sizeof(struct kinfo_file2))
+ break;
+
+ if (KREAD(kd, (u_long)p, &proc)) {
+ _kvm_err(kd, kd->program, "can't read proc at %x", p);
+ return (NULL);
+ }
+
+ /* skip system, embryonic and undead processes */
+ if ((proc.p_flag & P_SYSTEM) ||
+ proc.p_stat == SIDL || proc.p_stat == SZOMB)
+ continue;
+ if (op == KERN_FILE_BYPID) {
+ if (arg > 0 && proc.p_pid != (pid_t)arg) {
+ /* not the pid we are looking for */
+ continue;
+ }
+ } else /* if (op == KERN_FILE_BYUID) */ {
+ if (arg > 0 && proc.p_ucred->cr_uid != (uid_t)arg) {
+ /* not the uid we are looking for */
+ continue;
+ }
+ }
+
+ if (proc.p_fd == NULL || proc.p_p == NULL)
+ continue;
+
+ if (KREAD(kd, (u_long)proc.p_p, &process)) {
+ _kvm_err(kd, kd->program, "can't read process at %x",
+ proc.p_p);
+ return (NULL);
+ }
+ proc.p_p = &process;
+
+ if (KREAD(kd, (u_long)process.ps_cred, &pcred) == 0)
+ KREAD(kd, (u_long)pcred.pc_ucred, &ucred);
+ process.ps_cred = &pcred;
+ pcred.pc_ucred = &ucred;
+
+ if (KREAD(kd, (u_long)proc.p_fd, &filed0)) {
+ _kvm_err(kd, kd->program, "can't read filedesc at %x",
+ proc.p_fd);
+ return (NULL);
+ }
+ proc.p_fd = &filed;
+
+ if (filed.fd_cdir) {
+ if (buflen < sizeof(struct kinfo_file2))
+ goto done;
+ kf = (struct kinfo_file2 *)where;
+ where += sizeof(struct kinfo_file2);
+ buflen -= sizeof(struct kinfo_file2);
+ n++;
+ if (fill_file2(kd, kf, NULL, filed.fd_cdir, &proc,
+ KERN_FILE_CDIR) == -1)
+ return (NULL);
+ }
+ if (filed.fd_rdir) {
+ if (buflen < sizeof(struct kinfo_file2))
+ goto done;
+ kf = (struct kinfo_file2 *)where;
+ where += sizeof(struct kinfo_file2);
+ buflen -= sizeof(struct kinfo_file2);
+ n++;
+ if (fill_file2(kd, kf, NULL, filed.fd_rdir, &proc,
+ KERN_FILE_RDIR) == -1)
+ return (NULL);
+ }
+ if (proc.p_tracep) {
+ if (buflen < sizeof(struct kinfo_file2))
+ goto done;
+ kf = (struct kinfo_file2 *)where;
+ where += sizeof(struct kinfo_file2);
+ buflen -= sizeof(struct kinfo_file2);
+ n++;
+ if (fill_file2(kd, kf, NULL, proc.p_tracep, &proc,
+ KERN_FILE_TRACE) == -1)
+ return (NULL);
+ }
+
+ if (filed.fd_nfiles < 0 ||
+ filed.fd_lastfile >= filed.fd_nfiles ||
+ filed.fd_freefile > filed.fd_lastfile + 1) {
+ _kvm_err(kd, kd->program,
+ "filedesc corrupted at %x for pid %d",
+ proc.p_fd, proc.p_pid);
+ return (NULL);
+ }
+
+ for (i = 0; i < filed.fd_nfiles; i++) {
+ if (buflen < sizeof(struct kinfo_file2))
+ goto done;
+ if ((fp = filed.fd_ofiles[i]) == NULL)
+ continue;
+ if (KREAD(kd, (u_long)fp, &file)) {
+ _kvm_err(kd, kd->program, "can't read file");
+ return (NULL);
+ }
+ kf = (struct kinfo_file2 *)where;
+ where += sizeof(struct kinfo_file2);
+ buflen -= sizeof(struct kinfo_file2);
+ n++;
+ if (fill_file2(kd, kf, &file, NULL, &proc, i) == -1)
+ return (NULL);
+ }
+ }
+done:
+ *cnt = n;
+ return (kf);
+}
+
+#define PTRTOINT64(_x) ((u_int64_t)(u_long)(_x))
+
+static int
+fill_file2(kvm_t *kd, struct kinfo_file2 *kf, struct file *fp, struct vnode *vp,
+ struct proc *p, int fd)
+{
+ struct ucred f_cred;
+
+ memset(kf, 0, sizeof(*kf));
+
+ kf->fd_fd = fd; /* might not really be an fd */
+
+ if (fp != NULL) {
+ /* Fill in f_cred */
+ if (KREAD(kd, (u_long)fp->f_cred, &f_cred)) {
+ _kvm_err(kd, kd->program, "can't read f_cred");
+ return (-1);
+ }
+ fp->f_cred = &f_cred;
+
+ kf->f_fileaddr = PTRTOINT64(fp);
+ kf->f_flag = fp->f_flag;
+ kf->f_iflags = fp->f_iflags;
+ kf->f_type = fp->f_type;
+ kf->f_count = fp->f_count;
+ kf->f_msgcount = fp->f_msgcount;
+ kf->f_ucred = PTRTOINT64(fp->f_cred);
+ kf->f_uid = fp->f_cred->cr_uid;
+ kf->f_gid = fp->f_cred->cr_gid;
+ kf->f_ops = PTRTOINT64(fp->f_ops);
+ kf->f_offset = fp->f_offset;
+ kf->f_data = PTRTOINT64(fp->f_data);
+ kf->f_usecount = fp->f_usecount;
+
+ if (getuid() == 0 || p->p_ucred->cr_uid == fp->f_cred->cr_uid) {
+ kf->f_rxfer = fp->f_rxfer;
+ kf->f_rwfer = fp->f_wxfer;
+ kf->f_seek = fp->f_seek;
+ kf->f_rbytes = fp->f_rbytes;
+ kf->f_wbytes = fp->f_rbytes;
+ }
+ } else if (vp != NULL) {
+ /* fake it */
+ kf->f_type = DTYPE_VNODE;
+ kf->f_flag = FREAD;
+ if (fd == KERN_FILE_TRACE)
+ kf->f_flag |= FWRITE;
+ }
+
+ /* information about the object associated with this file */
+ switch (kf->f_type) {
+ case DTYPE_VNODE: {
+ struct vnode vbuf;
+ struct mount mount;
+
+ if (KREAD(kd, (u_long)(fp ? fp->f_data : vp), &vbuf)) {
+ _kvm_err(kd, kd->program, "can't read vnode");
+ return (-1);
+ }
+ vp = &vbuf;
+
+ if (KREAD(kd, (u_long)vp->v_mount, &mount)) {
+ _kvm_err(kd, kd->program, "can't read v_mount");
+ return (-1);
+ }
+ vp->v_mount = &mount;
+
+ kf->v_un = PTRTOINT64(vp->v_un.vu_socket);
+ kf->v_type = vp->v_type;
+ kf->v_tag = vp->v_tag;
+ kf->v_flag = vp->v_flag;
+ kf->v_data = PTRTOINT64(vp->v_data);
+ kf->v_mount = PTRTOINT64(vp->v_mount);
+ strlcpy(kf->f_mntonname, vp->v_mount->mnt_stat.f_mntonname,
+ sizeof(kf->f_mntonname));
+
+ /* Fill in va_fsid, va_fileid, va_mode, va_size, va_rdev */
+ filestat(kd, kf, vp);
+ break;
+ }
+
+ case DTYPE_SOCKET: {
+ struct socket sock;
+ struct protosw protosw;
+ struct domain domain;
+
+ if (KREAD(kd, (u_long)fp->f_data, &sock)) {
+ _kvm_err(kd, kd->program, "can't read socket");
+ return (-1);
+ }
+
+ kf->so_type = sock.so_type;
+ kf->so_state = sock.so_state;
+ kf->so_pcb = PTRTOINT64(sock.so_pcb);
+ if (KREAD(kd, (u_long)sock.so_proto, &protosw)) {
+ _kvm_err(kd, kd->program, "can't read protosw");
+ return (-1);
+ }
+ kf->so_protocol = protosw.pr_protocol;
+ if (KREAD(kd, (u_long)protosw.pr_domain, &domain)) {
+ _kvm_err(kd, kd->program, "can't read domain");
+ return (-1);
+ }
+ kf->so_family = domain.dom_family;
+ switch (kf->so_family) {
+ case AF_INET: {
+ struct inpcb inpcb;
+
+ if (KREAD(kd, (u_long)sock.so_pcb, &inpcb)) {
+ _kvm_err(kd, kd->program, "can't read inpcb");
+ return (-1);
+ }
+ kf->inp_ppcb = PTRTOINT64(inpcb.inp_ppcb);
+ kf->inp_lport = inpcb.inp_lport;
+ kf->inp_laddru[0] = inpcb.inp_laddr.s_addr;
+ kf->inp_fport = inpcb.inp_fport;
+ kf->inp_faddru[0] = inpcb.inp_faddr.s_addr;
+ break;
+ }
+ case AF_INET6: {
+ struct inpcb inpcb;
+#define s6_addr32 __u6_addr.__u6_addr32
+
+ if (KREAD(kd, (u_long)sock.so_pcb, &inpcb)) {
+ _kvm_err(kd, kd->program, "can't read inpcb");
+ return (-1);
+ }
+ kf->inp_ppcb = PTRTOINT64(inpcb.inp_ppcb);
+ kf->inp_lport = inpcb.inp_lport;
+ kf->inp_laddru[0] = inpcb.inp_laddr6.s6_addr32[0];
+ kf->inp_laddru[1] = inpcb.inp_laddr6.s6_addr32[1];
+ kf->inp_laddru[2] = inpcb.inp_laddr6.s6_addr32[2];
+ kf->inp_laddru[3] = inpcb.inp_laddr6.s6_addr32[3];
+ kf->inp_fport = inpcb.inp_fport;
+ kf->inp_faddru[0] = inpcb.inp_laddr6.s6_addr32[0];
+ kf->inp_faddru[1] = inpcb.inp_faddr6.s6_addr32[1];
+ kf->inp_faddru[2] = inpcb.inp_faddr6.s6_addr32[2];
+ kf->inp_faddru[3] = inpcb.inp_faddr6.s6_addr32[3];
+ break;
+ }
+ case AF_UNIX: {
+ struct unpcb unpcb;
+
+ if (KREAD(kd, (u_long)sock.so_pcb, &unpcb)) {
+ _kvm_err(kd, kd->program, "can't read unpcb");
+ return (-1);
+ }
+ kf->unp_conn = PTRTOINT64(unpcb.unp_conn);
+ break;
+ }
+ }
+ break;
+ }
+
+ case DTYPE_PIPE: {
+ struct pipe pipe;
+
+ if (KREAD(kd, (u_long)fp->f_data, &pipe)) {
+ _kvm_err(kd, kd->program, "can't read pipe");
+ return (-1);
+ }
+ kf->pipe_peer = PTRTOINT64(pipe.pipe_peer);
+ kf->pipe_state = pipe.pipe_state;
+ break;
+ }
+
+ case DTYPE_KQUEUE: {
+ struct kqueue kqi;
+
+ if (KREAD(kd, (u_long)fp->f_data, &kqi)) {
+ _kvm_err(kd, kd->program, "can't read kqi");
+ return (-1);
+ }
+ kf->kq_count = kqi.kq_count;
+ kf->kq_state = kqi.kq_state;
+ break;
+ }
+ case DTYPE_SYSTRACE: {
+ struct fsystrace f;
+
+ if (KREAD(kd, (u_long)fp->f_data, &f)) {
+ _kvm_err(kd, kd->program, "can't read fsystrace");
+ return (-1);
+ }
+ kf->str_npolicies = f.npolicies;
+ break;
+ }
+ }
+
+ /* per-process information for KERN_FILE_BY[PU]ID */
+ if (p != NULL) {
+ kf->p_pid = p->p_pid;
+ kf->p_uid = p->p_ucred->cr_uid;
+ kf->p_gid = p->p_ucred->cr_gid;
+ strlcpy(kf->p_comm, p->p_comm, sizeof(kf->p_comm));
+ if (p->p_fd != NULL)
+ kf->fd_ofileflags = p->p_fd->fd_ofileflags[fd];
+ }
+
+ return (0);
+}
+
+static int
+ufs_filestat(kvm_t *kd, struct kinfo_file2 *kf, struct vnode *vp)
+{
+ struct inode inode;
+ struct ufs1_dinode di1;
+
+ if (KREAD(kd, (u_long)VTOI(vp), &inode)) {
+ _kvm_err(kd, kd->program, "can't read inode at %p", VTOI(vp));
+ return (-1);
+ }
+
+ if (KREAD(kd, (u_long)inode.i_din1, &di1)) {
+ _kvm_err(kd, kd->program, "can't read dinode at %p",
+ inode.i_din1);
+ return (-1);
+ }
+
+ inode.i_din1 = &di1;
+
+ kf->va_fsid = inode.i_dev & 0xffff;
+ kf->va_fileid = (long)inode.i_number;
+ kf->va_mode = inode.i_ffs1_mode;
+ kf->va_size = inode.i_ffs1_size;
+ kf->va_rdev = inode.i_ffs1_rdev;
+
+ return (0);
+}
+
+static int
+ext2fs_filestat(kvm_t *kd, struct kinfo_file2 *kf, struct vnode *vp)
+{
+ struct inode inode;
+ struct ext2fs_dinode e2di;
+
+ if (KREAD(kd, (u_long)VTOI(vp), &inode)) {
+ _kvm_err(kd, kd->program, "can't read inode at %p", VTOI(vp));
+ return (-1);
+ }
+
+ if (KREAD(kd, (u_long)inode.i_e2din, &e2di)) {
+ _kvm_err(kd, kd->program, "can't read dinode at %p",
+ inode.i_e2din);
+ return (-1);
+ }
+
+ inode.i_e2din = &e2di;
+
+ kf->va_fsid = inode.i_dev & 0xffff;
+ kf->va_fileid = (long)inode.i_number;
+ kf->va_mode = inode.i_e2fs_mode;
+ kf->va_size = inode.i_e2fs_size;
+ kf->va_rdev = 0; /* XXX */
+
+ return (0);
+}
+
+static int
+msdos_filestat(kvm_t *kd, struct kinfo_file2 *kf, struct vnode *vp)
+{
+#if 0 /* XXX */
+ struct inode inode;
+
+ if (KREAD(kd, (u_long)VTOI(vp), &inode)) {
+ _kvm_err(kd, kd->program, "can't read inode at %p", VTOI(vp));
+ return (-1);
+ }
+ kf->va_fsid = inode.i_dev & 0xffff;
+ kf->va_fileid = (long)inode.i_number;
+ kf->va_mode = inode.i_e2fs_mode;
+ kf->va_size = inode.i_e2fs_size;
+ kf->va_rdev = 0; /* XXX */
+#endif
+
+ return (0);
+}
+
+static int
+nfs_filestat(kvm_t *kd, struct kinfo_file2 *kf, struct vnode *vp)
+{
+ struct nfsnode nfsnode;
+ mode_t mode;
+
+ if (KREAD(kd, (u_long)VTONFS(vp), &nfsnode)) {
+ _kvm_err(kd, kd->program, "can't read nfsnode at %p",
+ VTONFS(vp));
+ return (-1);
+ }
+ kf->va_fsid = nfsnode.n_vattr.va_fsid;
+ kf->va_fileid = nfsnode.n_vattr.va_fileid;
+ kf->va_size = nfsnode.n_size;
+ kf->va_rdev = nfsnode.n_vattr.va_rdev;
+ mode = (mode_t)nfsnode.n_vattr.va_mode;
+ switch (vp->v_type) {
+ case VREG:
+ mode |= S_IFREG;
+ break;
+ case VDIR:
+ mode |= S_IFDIR;
+ break;
+ case VBLK:
+ mode |= S_IFBLK;
+ break;
+ case VCHR:
+ mode |= S_IFCHR;
+ break;
+ case VLNK:
+ mode |= S_IFLNK;
+ break;
+ case VSOCK:
+ mode |= S_IFSOCK;
+ break;
+ case VFIFO:
+ mode |= S_IFIFO;
+ break;
+ default:
+ break;
+ }
+ kf->va_mode = mode;
+
+ return (0);
+}
+
+static int
+nnpfs_filestat(kvm_t *kd, struct kinfo_file2 *kf, struct vnode *vp)
+{
+ struct nnpfs_node nnpfs_node;
+
+ if (KREAD(kd, (u_long)VNODE_TO_XNODE(vp), &nnpfs_node)) {
+ _kvm_err(kd, kd->program, "can't read nnpfs_node at %p",
+ VTOI(vp));
+ return (-1);
+ }
+ kf->va_fsid = nnpfs_node.attr.va_fsid;
+ kf->va_fileid = (long)nnpfs_node.attr.va_fileid;
+ kf->va_mode = nnpfs_node.attr.va_mode;
+ kf->va_size = nnpfs_node.attr.va_size;
+ kf->va_rdev = nnpfs_node.attr.va_rdev;
+
+ return (0);
+}
+
+static int
+spec_filestat(kvm_t *kd, struct kinfo_file2 *kf, struct vnode *vp)
+{
+ struct specinfo specinfo;
+ struct vnode parent;
+
+ if (KREAD(kd, (u_long)vp->v_specinfo, &specinfo)) {
+ _kvm_err(kd, kd->program, "can't read specinfo at %p",
+ vp->v_specinfo);
+ return (-1);
+ }
+
+ vp->v_specinfo = &specinfo;
+
+ if (KREAD(kd, (u_long)vp->v_specparent, &parent)) {
+ _kvm_err(kd, kd->program, "can't read parent vnode at %p",
+ vp->v_specparent);
+ return (-1);
+ }
+
+ if (ufs_filestat(kd, kf, vp))
+ return (-1);
+
+ return (0);
+}
+
+static int
+isofs_filestat(kvm_t *kd, struct kinfo_file2 *kf, struct vnode *vp)
+{
+ struct iso_node inode;
+
+ if (KREAD(kd, (u_long)VTOI(vp), &inode)) {
+ _kvm_err(kd, kd->program, "can't read inode at %p", VTOI(vp));
+ return (-1);
+ }
+ kf->va_fsid = inode.i_dev & 0xffff;
+ kf->va_fileid = (long)inode.i_number;
+ kf->va_mode = inode.inode.iso_mode;
+ kf->va_size = inode.i_size;
+ kf->va_rdev = inode.i_dev;
+
+ return (0);
+}
+
+static int
+filestat(kvm_t *kd, struct kinfo_file2 *kf, struct vnode *vp)
+{
+ int ret = 0;
+
+ if (vp->v_type != VNON && vp->v_type != VBAD) {
+ switch (vp->v_tag) {
+ case VT_UFS:
+ case VT_MFS:
+ ret = ufs_filestat(kd, kf, vp);
+ break;
+ case VT_NFS:
+ ret = nfs_filestat(kd, kf, vp);
+ break;
+ case VT_EXT2FS:
+ ret = ext2fs_filestat(kd, kf, vp);
+ break;
+ case VT_ISOFS:
+ ret = isofs_filestat(kd, kf, vp);
+ break;
+ case VT_MSDOSFS:
+ ret = msdos_filestat(kd, kf, vp);
+ break;
+ case VT_NNPFS:
+ ret = nnpfs_filestat(kd, kf, vp);
+ break;
+ case VT_NON:
+ if (vp->v_flag & VCLONE)
+ ret = spec_filestat(kd, kf, vp);
+ break;
+ }
+ }
+ return (ret);
+}
diff --git a/lib/libkvm/kvm_getfiles.3 b/lib/libkvm/kvm_getfiles.3
index abd19fa9d3d..3ae45646bd9 100644
--- a/lib/libkvm/kvm_getfiles.3
+++ b/lib/libkvm/kvm_getfiles.3
@@ -1,4 +1,4 @@
-.\" $OpenBSD: kvm_getfiles.3,v 1.11 2007/05/31 19:19:35 jmc Exp $
+.\" $OpenBSD: kvm_getfiles.3,v 1.12 2009/06/07 03:09:34 millert Exp $
.\" $NetBSD: kvm_getfiles.3,v 1.3 1996/03/18 22:33:23 thorpej Exp $
.\"
.\" Copyright (c) 1992, 1993
@@ -34,7 +34,7 @@
.\"
.\" @(#)kvm_getfiles.3 8.2 (Berkeley) 4/19/94
.\"
-.Dd $Mdocdate: May 31 2007 $
+.Dd $Mdocdate: June 7 2009 $
.Dt KVM_GETFILES 3
.Os
.Sh NAME
@@ -49,6 +49,8 @@
.\" .Fa kvm_t *kd
.Ft char *
.Fn kvm_getfiles "kvm_t *kd" "int op" "int arg" "int *cnt"
+.Ft struct kinfo_file2 *
+.Fn kvm_getfile2 "kvm_t *kd" "int op" "int arg" "size_t elemsize" "int *cnt"
.Sh DESCRIPTION
.Fn kvm_getfiles
returns a (sub-)set of the open files in the kernel indicated by
@@ -68,8 +70,62 @@ preceded by the address of the first file entry in the kernel.
This memory is owned by kvm and is not guaranteed to be persistent across
subsequent kvm library calls.
Data should be copied out if it needs to be saved.
+.Pp
+.Fn kvm_getfile2
+is similar to
+.Fn kvm_getfiles
+but returns an array of
+.Vt kinfo_file2
+structures.
+Additionally, only the first
+.Fa elemsize
+bytes of each array entry are returned.
+If the size of the
+.Vt kinfo_file2
+structure increases in size in a future release of
+.Ox ,
+the kernel will only return the requested amount of data for
+each array entry and programs that use
+.Fn kvm_getfile2
+will continue to function without the need for recompilation.
+The
+.Fa op
+and
+.Fa arg
+arguments constitute a predicate which limits the set of processes returned.
+The value of
+.Fa op
+describes the filtering predicate as follows:
+.Pp
+.Bl -tag -width 20n -offset indent -compact
+.It Dv KERN_FILE_BYFILE
+all open files
+.It Dv KERN_PROC_BYPID
+files opened by process ID
+.Fa arg
+(\-1 for all processes)
+.It Dv KERN_FILE_BYUID
+files opened by processes with effective user ID
+.Fa arg
+.El
+.Pp
+Files associated with a process will include information about
+the process that has the file open.
+.Pp
+The files are returned as a contiguous array of
+.Vt kinfo_file2
+structures.
+The number of structures found is returned in the reference parameter
+.Fa cnt .
+This memory is owned by kvm and will be overwritten by subsequent calls to
+.Fn kvm_getfile2
+and destroyed by
+.Fn kvm_close .
+Data should be copied out if it needs to be saved.
.Sh RETURN VALUES
.Fn kvm_getfiles
+and
+.Fn kvm_getfile2
will return
.Dv NULL
on failure.
diff --git a/lib/libkvm/kvm_private.h b/lib/libkvm/kvm_private.h
index fa0d714f9cd..6783f3b4ad9 100644
--- a/lib/libkvm/kvm_private.h
+++ b/lib/libkvm/kvm_private.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: kvm_private.h,v 1.11 2006/03/31 03:59:40 deraadt Exp $ */
+/* $OpenBSD: kvm_private.h,v 1.12 2009/06/07 03:09:34 millert Exp $ */
/* $NetBSD: kvm_private.h,v 1.7 1996/05/05 04:32:15 gwr Exp $ */
/*-
@@ -52,6 +52,7 @@ struct __kvm {
int nlfd; /* namelist file (e.g., /vmunix) */
struct kinfo_proc *procbase;
struct kinfo_proc2 *procbase2;
+ struct kinfo_file *filebase;
int nbpg; /* page size */
char *swapspc; /* (dynamic) storage for swapped pages */
char *argspc, *argbuf; /* (dynamic) storage for argv strings */
diff --git a/lib/libkvm/shlib_version b/lib/libkvm/shlib_version
index 1c5d96eb2aa..7ff575df806 100644
--- a/lib/libkvm/shlib_version
+++ b/lib/libkvm/shlib_version
@@ -1,2 +1,2 @@
major=9
-minor=0
+minor=1