Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 12 May 2011 10:11:39 +0000 (UTC)
From:      Stanislav Sedov <stas@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r221807 - in head: lib lib/libprocstat lib/libprocstat/zfs lib/libutil sys/kern sys/sys usr.bin/fstat usr.bin/fstat/zfs usr.bin/procstat
Message-ID:  <201105121011.p4CABdkM030432@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: stas
Date: Thu May 12 10:11:39 2011
New Revision: 221807
URL: http://svn.freebsd.org/changeset/base/221807

Log:
  - Commit work from libprocstat project.  These patches add support for runtime
    file and processes information retrieval from the running kernel via sysctl
    in the form of new library, libprocstat.  The library also supports KVM backend
    for analyzing memory crash dumps.  Both procstat(1) and fstat(1) utilities have
    been modified to take advantage of the library (as the bonus point the fstat(1)
    utility no longer need superuser privileges to operate), and the procstat(1)
    utility is now able to display information from memory dumps as well.
  
    The newly introduced fuser(1) utility also uses this library and able to operate
    via sysctl and kvm backends.
  
    The library is by no means complete (e.g. KVM backend is missing vnode name
    resolution routines, and there're no manpages for the library itself) so I
    plan to improve it further.  I'm commiting it so it will get wider exposure
    and review.
  
    We won't be able to MFC this work as it relies on changes in HEAD, which
    was introduced some time ago, that break kernel ABI.  OTOH we may be able
    to merge the library with KVM backend if we really need it there.
  
  Discussed with:	rwatson

Added:
  head/lib/libprocstat/
  head/lib/libprocstat/Makefile   (contents, props changed)
  head/lib/libprocstat/cd9660.c   (contents, props changed)
  head/lib/libprocstat/common_kvm.c   (contents, props changed)
  head/lib/libprocstat/common_kvm.h   (contents, props changed)
  head/lib/libprocstat/libprocstat.c   (contents, props changed)
  head/lib/libprocstat/libprocstat.h   (contents, props changed)
  head/lib/libprocstat/libprocstat_internal.h   (contents, props changed)
  head/lib/libprocstat/msdosfs.c   (contents, props changed)
  head/lib/libprocstat/ntfs.c   (contents, props changed)
  head/lib/libprocstat/nwfs.c   (contents, props changed)
  head/lib/libprocstat/smbfs.c   (contents, props changed)
  head/lib/libprocstat/udf.c   (contents, props changed)
  head/lib/libprocstat/zfs/
  head/lib/libprocstat/zfs.c   (contents, props changed)
  head/lib/libprocstat/zfs/Makefile   (contents, props changed)
  head/lib/libutil/kinfo_getallproc.3   (contents, props changed)
  head/lib/libutil/kinfo_getallproc.c   (contents, props changed)
  head/lib/libutil/kinfo_getproc.3   (contents, props changed)
  head/lib/libutil/kinfo_getproc.c   (contents, props changed)
  head/usr.bin/fstat/functions.h   (contents, props changed)
  head/usr.bin/fstat/fuser.1   (contents, props changed)
  head/usr.bin/fstat/fuser.c   (contents, props changed)
  head/usr.bin/fstat/main.c   (contents, props changed)
Deleted:
  head/usr.bin/fstat/cd9660.c
  head/usr.bin/fstat/fstat.h
  head/usr.bin/fstat/msdosfs.c
  head/usr.bin/fstat/zfs/
  head/usr.bin/fstat/zfs.c
Modified:
  head/lib/Makefile
  head/lib/libutil/Makefile
  head/lib/libutil/libutil.h
  head/sys/kern/kern_descrip.c
  head/sys/kern/kern_proc.c
  head/sys/sys/user.h
  head/usr.bin/fstat/Makefile
  head/usr.bin/fstat/fstat.c
  head/usr.bin/procstat/Makefile
  head/usr.bin/procstat/procstat.c
  head/usr.bin/procstat/procstat.h
  head/usr.bin/procstat/procstat_args.c
  head/usr.bin/procstat/procstat_basic.c
  head/usr.bin/procstat/procstat_bin.c
  head/usr.bin/procstat/procstat_cred.c
  head/usr.bin/procstat/procstat_files.c
  head/usr.bin/procstat/procstat_kstack.c
  head/usr.bin/procstat/procstat_sigs.c
  head/usr.bin/procstat/procstat_threads.c
  head/usr.bin/procstat/procstat_vm.c

Modified: head/lib/Makefile
==============================================================================
--- head/lib/Makefile	Thu May 12 10:11:24 2011	(r221806)
+++ head/lib/Makefile	Thu May 12 10:11:39 2011	(r221807)
@@ -92,6 +92,7 @@ SUBDIR=	${SUBDIR_ORDERED} \
 	${_libpkg} \
 	${_libpmc} \
 	${_libproc} \
+	libprocstat \
 	librt \
 	${_librtld_db} \
 	${_libsdp} \

Added: head/lib/libprocstat/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libprocstat/Makefile	Thu May 12 10:11:39 2011	(r221807)
@@ -0,0 +1,36 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+LIB=	procstat
+
+SRCS=	cd9660.c	\
+	common_kvm.c	\
+	libprocstat.c	\
+        msdosfs.c	\
+	ntfs.c		\
+	nwfs.c		\
+	smbfs.c		\
+	udf.c
+
+INCS=	libprocstat.h
+CFLAGS+=	-I. -I${.CURDIR} -D_KVM_VNODE
+SHLIB_MAJOR=	1
+WITHOUT_MAN=	yes
+
+# XXX This is a hack.
+.if ${MK_CDDL} != "no"
+CFLAGS+=	-DZFS
+OBJS+=	zfs/zfs.o
+SOBJS+=	zfs/zfs.So
+POBJS+=	zfs/zfs.po
+SUBDIR=	zfs
+zfs/zfs.o: .PHONY
+	@cd ${.CURDIR}/zfs && ${MAKE} zfs.o
+zfs/zfs.So: .PHONY
+	@cd ${.CURDIR}/zfs && ${MAKE} zfs.So
+zfs/zfs.po: .PHONY
+	@cd ${.CURDIR}/zfs && ${MAKE} zfs.po
+.endif
+
+.include <bsd.lib.mk>

Added: head/lib/libprocstat/cd9660.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libprocstat/cd9660.c	Thu May 12 10:11:39 2011	(r221807)
@@ -0,0 +1,90 @@
+/* 
+ * Copyright (c) 2000 Peter Edwards
+ * Copyright (c) 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by Peter Edwards
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+/*
+ * XXX -
+ * This had to be separated from fstat.c because cd9660s has namespace
+ * conflicts with UFS.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+
+#include <isofs/cd9660/cd9660_node.h>
+#define _KERNEL
+#include <isofs/cd9660/iso.h>
+#undef _KERNEL
+
+#include <kvm.h>
+#include <stdio.h>
+
+#include "libprocstat.h"
+#include "common_kvm.h"
+
+int
+isofs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn)
+{
+	struct iso_node isonode;
+	struct iso_mnt mnt;
+
+	if (!kvm_read_all(kd, (unsigned long)VTOI(vp), &isonode,
+	    sizeof(isonode))) {
+		warnx("can't read iso_node at %p",
+		    (void *)VTOI(vp));
+		return (1);
+	}
+	if (!kvm_read_all(kd, (unsigned long)isonode.i_mnt, &mnt,
+	    sizeof(mnt))) {
+		warnx("can't read iso_mnt at %p",
+		    (void *)VTOI(vp));
+		return (1);
+	}
+	vn->vn_fsid = dev2udev(kd, mnt.im_dev);
+	vn->vn_mode = (mode_t)isonode.inode.iso_mode;
+	vn->vn_fileid = (long)isonode.i_number;
+	vn->vn_size = (u_long)isonode.i_size;
+	return (0);
+}

Added: head/lib/libprocstat/common_kvm.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libprocstat/common_kvm.c	Thu May 12 10:11:39 2011	(r221807)
@@ -0,0 +1,207 @@
+/*-
+ * Copyright (c) 2009 Stanislav Sedov <stas@FreeBSD.org>
+ * Copyright (c) 1988, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/conf.h>
+#define	_KERNEL
+#include <sys/pipe.h>
+#include <sys/mount.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <fs/devfs/devfs.h>
+#include <fs/devfs/devfs_int.h>
+#undef _KERNEL
+#include <nfs/nfsproto.h>
+#include <nfsclient/nfs.h>
+#include <nfsclient/nfsnode.h>
+
+#include <assert.h>
+#include <err.h>
+#include <kvm.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <libprocstat.h>
+#include "common_kvm.h"
+
+int
+kvm_read_all(kvm_t *kd, unsigned long addr, void *buf, size_t nbytes)
+{
+	ssize_t error;
+
+	if (nbytes >= SSIZE_MAX)
+		return (0);
+	error = kvm_read(kd, addr, buf, nbytes);
+	return (error == (ssize_t)(nbytes));
+}
+
+int
+kdevtoname(kvm_t *kd, struct cdev *dev, char *buf)
+{
+	struct cdev si;
+
+	assert(buf);
+	if (!kvm_read_all(kd, (unsigned long)dev, &si, sizeof(si)))
+		return (1);
+	strlcpy(buf, si.__si_namebuf, SPECNAMELEN + 1);
+	return (0);
+}
+
+int
+ufs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn)
+{
+	struct inode inode;
+
+	if (!kvm_read_all(kd, (unsigned long)VTOI(vp), &inode, sizeof(inode))) {
+		warnx("can't read inode at %p", (void *)VTOI(vp));
+		return (1);
+	}
+	/*
+	 * The st_dev from stat(2) is a dev_t. These kernel structures
+	 * contain cdev pointers. We need to convert to dev_t to make
+	 * comparisons
+	 */
+	vn->vn_fsid = dev2udev(kd, inode.i_dev);
+	vn->vn_fileid = (long)inode.i_number;
+	vn->vn_mode = (mode_t)inode.i_mode;
+	vn->vn_size = (u_long)inode.i_size;
+	return (0);
+}
+
+int
+devfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn)
+{
+	struct devfs_dirent devfs_dirent;
+	struct mount mount;
+
+	if (!kvm_read_all(kd, (unsigned long)getvnodedata(vp), &devfs_dirent,
+	    sizeof(devfs_dirent))) {
+		warnx("can't read devfs_dirent at %p",
+		    (void *)vp->v_data);
+		return (1);
+	}
+	if (!kvm_read_all(kd, (unsigned long)getvnodemount(vp), &mount,
+	    sizeof(mount))) {
+		warnx("can't read mount at %p",
+		    (void *)getvnodemount(vp));
+		return (1);
+	}
+	vn->vn_fsid = mount.mnt_stat.f_fsid.val[0];
+	vn->vn_fileid = devfs_dirent.de_inode;
+	vn->vn_mode = (devfs_dirent.de_mode & ~S_IFMT) | S_IFCHR;
+	vn->vn_size = 0;
+	return (0);
+}
+
+int
+nfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn)
+{
+	struct nfsnode nfsnode;
+	mode_t mode;
+
+	if (!kvm_read_all(kd, (unsigned long)VTONFS(vp), &nfsnode,
+	    sizeof(nfsnode))) {
+		warnx("can't read nfsnode at %p",
+		    (void *)VTONFS(vp));
+		return (1);
+	}
+	vn->vn_fsid = nfsnode.n_vattr.va_fsid;
+	vn->vn_fileid = nfsnode.n_vattr.va_fileid;
+	vn->vn_size = nfsnode.n_size;
+	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;
+	};
+	vn->vn_mode = mode;
+	return (0);
+}
+
+/*
+ * Read the cdev structure in the kernel in order to work out the
+ * associated dev_t
+ */
+dev_t
+dev2udev(kvm_t *kd, struct cdev *dev)
+{
+	struct cdev_priv priv;
+
+	assert(kd);
+	if (kvm_read_all(kd, (unsigned long)cdev2priv(dev), &priv,
+	    sizeof(priv))) {
+		return ((dev_t)priv.cdp_inode);
+	} else {
+		warnx("can't convert cdev *%p to a dev_t\n", dev);
+		return (-1);
+	}
+}
+
+void *
+getvnodedata(struct vnode *vp)
+{
+	return (vp->v_data);
+}
+
+struct mount *
+getvnodemount(struct vnode *vp)
+{
+	return (vp->v_mount);
+}

Added: head/lib/libprocstat/common_kvm.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libprocstat/common_kvm.h	Thu May 12 10:11:39 2011	(r221807)
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2009 Stanislav Sedov <stas@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	_COMMON_KVM_H_
+#define	_COMMON_KVM_H_
+
+dev_t	dev2udev(kvm_t *kd, struct cdev *dev);
+int	kdevtoname(kvm_t *kd, struct cdev *dev, char *);
+int	kvm_read_all(kvm_t *kd, unsigned long addr, void *buf,
+    size_t nbytes);
+
+/*
+ * Filesystems specific access routines.
+ */
+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	ntfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
+int	nwfs_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);
+void	*getvnodedata(struct vnode *vp);
+struct mount	*getvnodemount(struct vnode *vp);
+
+#endif	/* _COMMON_KVM_H_ */

Added: head/lib/libprocstat/libprocstat.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libprocstat/libprocstat.c	Thu May 12 10:11:39 2011	(r221807)
@@ -0,0 +1,1306 @@
+/*-
+ * Copyright (c) 2009 Stanislav Sedov <stas@FreeBSD.org>
+ * Copyright (c) 1988, 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the University of
+ *      California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/un.h>
+#include <sys/unpcb.h>
+#include <sys/sysctl.h>
+#include <sys/tty.h>
+#include <sys/filedesc.h>
+#include <sys/queue.h>
+#define	_WANT_FILE
+#include <sys/file.h>
+#include <sys/conf.h>
+#define	_KERNEL
+#include <sys/mount.h>
+#include <sys/pipe.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <fs/devfs/devfs.h>
+#include <fs/devfs/devfs_int.h>
+#undef _KERNEL
+#include <nfs/nfsproto.h>
+#include <nfsclient/nfs.h>
+#include <nfsclient/nfsnode.h>
+
+#include <vm/vm.h>
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <libutil.h>
+#include <limits.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <libprocstat.h>
+#include "libprocstat_internal.h"
+#include "common_kvm.h"
+
+int     statfs(const char *, struct statfs *);	/* XXX */
+
+#define	PROCSTAT_KVM	1
+#define	PROCSTAT_SYSCTL	2
+
+static char	*getmnton(kvm_t *kd, struct mount *m);
+static struct filestat_list	*procstat_getfiles_kvm(
+    struct procstat *procstat, struct kinfo_proc *kp, int mmapped);
+static struct filestat_list	*procstat_getfiles_sysctl(
+    struct procstat *procstat, struct kinfo_proc *kp, int mmapped);
+static int	procstat_get_pipe_info_sysctl(struct filestat *fst,
+    struct pipestat *pipe, char *errbuf);
+static int	procstat_get_pipe_info_kvm(kvm_t *kd, struct filestat *fst,
+    struct pipestat *pipe, char *errbuf);
+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_socket_info_sysctl(struct filestat *fst,
+    struct sockstat *sock, char *errbuf);
+static int	procstat_get_socket_info_kvm(kvm_t *kd, struct filestat *fst,
+    struct sockstat *sock, char *errbuf);
+static int	to_filestat_flags(int flags);
+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 int	vntype2psfsttype(int type);
+
+void
+procstat_close(struct procstat *procstat)
+{
+
+	assert(procstat);
+	if (procstat->type == PROCSTAT_KVM)
+		kvm_close(procstat->kd);
+}
+
+struct procstat *
+procstat_open_sysctl(void)
+{
+	struct procstat *procstat;
+
+	procstat = calloc(1, sizeof(*procstat));
+	if (procstat == NULL) {
+		warn("malloc()");
+		return (NULL);
+	}
+	procstat->type = PROCSTAT_SYSCTL;
+	return (procstat);
+}
+
+struct procstat *
+procstat_open_kvm(const char *nlistf, const char *memf)
+{
+	struct procstat *procstat;
+	kvm_t *kd;
+	char buf[_POSIX2_LINE_MAX];
+
+	procstat = calloc(1, sizeof(*procstat));
+	if (procstat == NULL) {
+		warn("malloc()");
+		return (NULL);
+	}
+	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);
+	if (kd == NULL) {
+		warnx("kvm_openfiles(): %s", buf);
+		free(procstat);
+		return (NULL);
+	}
+	procstat->type = PROCSTAT_KVM;
+	procstat->kd = kd;
+	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;
+	int name[4];
+	int error;
+
+	assert(procstat);
+	assert(count);
+	p = NULL;
+	if (procstat->type == PROCSTAT_KVM) {
+		p0 = kvm_getprocs(procstat->kd, what, arg, count);
+		if (p0 == NULL || count == 0)
+			return (NULL);
+		len = *count * sizeof(*p);
+		p = malloc(len);
+		if (p == NULL) {
+			warnx("malloc(%zd)", len);
+			goto fail;
+		}
+		bcopy(p0, p, len);
+		return (p);
+	} else if (procstat->type == PROCSTAT_SYSCTL) {
+		len = 0;
+		name[0] = CTL_KERN;
+		name[1] = KERN_PROC;
+		name[2] = what;
+		name[3] = arg;
+		error = sysctl(name, 4, NULL, &len, NULL, 0);
+		if (error < 0 && errno != EPERM) {
+			warn("sysctl(kern.proc)");
+			goto fail;
+		}
+		if (len == 0) {
+			warnx("no processes?");
+			goto fail;
+		}
+		p = malloc(len);
+		if (p == NULL) {
+			warnx("malloc(%zd)", len);
+			goto fail;
+		}
+		error = sysctl(name, 4, p, &len, NULL, 0);
+		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");
+			goto fail;
+		}
+		*count = len / sizeof(*p);
+		return (p);
+	} else {
+		warnx("unknown access method");
+		return (NULL);
+	}
+fail:
+	if (p)
+		free(p);
+	return (NULL);
+}
+
+void
+procstat_freeprocs(struct procstat *procstat __unused, struct kinfo_proc *p)
+{
+
+	if (p != NULL)
+		free(p);
+	p = NULL;
+}
+
+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)
+		 return (procstat_getfiles_kvm(procstat, kp, mmapped));
+	else
+		return (NULL);
+}
+
+void
+procstat_freefiles(struct procstat *procstat, struct filestat_list *head)
+{
+	struct filestat *fst, *tmp;
+
+	STAILQ_FOREACH_SAFE(fst, head, next, tmp) {
+		if (fst->fs_path != NULL)
+			free(fst->fs_path);
+		free(fst);
+	}
+	free(head);
+	if (procstat->vmentries != NULL) {
+		free (procstat->vmentries);
+		procstat->vmentries = NULL;
+	}
+	if (procstat->files != NULL) {
+		free (procstat->files);
+		procstat->files = NULL;
+	}
+}
+
+static struct filestat *
+filestat_new_entry(void *typedep, int type, int fd, int fflags, int uflags,
+    int refcount, off_t offset, char *path)
+{
+	struct filestat *entry;
+
+	entry = calloc(1, sizeof(*entry));
+	if (entry == NULL) {
+		warn("malloc()");
+		return (NULL);
+	}
+	entry->fs_typedep = typedep;
+	entry->fs_fflags = fflags;
+	entry->fs_uflags = uflags;
+	entry->fs_fd = fd;
+	entry->fs_type = type;
+	entry->fs_ref_count = refcount;
+	entry->fs_offset = offset;
+	entry->fs_path = path;
+	return (entry);
+}
+
+static struct vnode *
+getctty(kvm_t *kd, struct kinfo_proc *kp)
+{
+	struct pgrp pgrp;
+	struct proc proc;
+	struct session sess;
+	int error;
+                        
+	assert(kp);
+	error = kvm_read_all(kd, (unsigned long)kp->ki_paddr, &proc,
+	    sizeof(proc));
+	if (error == 0) {
+		warnx("can't read proc struct at %p for pid %d",
+		    kp->ki_paddr, kp->ki_pid);
+		return (NULL);
+	}
+	if (proc.p_pgrp == NULL)
+		return (NULL);
+	error = kvm_read_all(kd, (unsigned long)proc.p_pgrp, &pgrp,
+	    sizeof(pgrp));
+	if (error == 0) {
+		warnx("can't read pgrp struct at %p for pid %d",
+		    proc.p_pgrp, kp->ki_pid);
+		return (NULL);
+	}
+	error = kvm_read_all(kd, (unsigned long)pgrp.pg_session, &sess,
+	    sizeof(sess));
+	if (error == 0) {
+		warnx("can't read session struct at %p for pid %d",
+		    pgrp.pg_session, kp->ki_pid);
+		return (NULL);
+	}
+	return (sess.s_ttyvp);
+}
+
+static struct filestat_list *
+procstat_getfiles_kvm(struct procstat *procstat, struct kinfo_proc *kp, int mmapped)
+{
+	struct file file;
+	struct filedesc filed;
+	struct vm_map_entry vmentry;
+	struct vm_object object;
+	struct vmspace vmspace;
+	vm_map_entry_t entryp;
+	vm_map_t map;
+	vm_object_t objp;
+	struct vnode *vp;
+	struct file **ofiles;
+	struct filestat *entry;
+	struct filestat_list *head;
+	kvm_t *kd;
+	void *data;
+	int i, fflags;
+	int prot, type;
+	unsigned int nfiles;
+
+	assert(procstat);
+	kd = procstat->kd;
+	if (kd == NULL)
+		return (NULL);
+	if (kp->ki_fd == NULL)
+		return (NULL);
+	if (!kvm_read_all(kd, (unsigned long)kp->ki_fd, &filed,
+	    sizeof(filed))) {
+		warnx("can't read filedesc at %p", (void *)kp->ki_fd);
+		return (NULL);
+	}
+
+	/*
+	 * Allocate list head.
+	 */
+	head = malloc(sizeof(*head));
+	if (head == NULL)
+		return (NULL);
+	STAILQ_INIT(head);
+
+	/* root directory vnode, if one. */
+	if (filed.fd_rdir) {
+		entry = filestat_new_entry(filed.fd_rdir, PS_FST_TYPE_VNODE, -1,
+		    PS_FST_FFLAG_READ, PS_FST_UFLAG_RDIR, 0, 0, NULL);
+		if (entry != NULL)
+			STAILQ_INSERT_TAIL(head, entry, next);
+	}
+	/* current working directory vnode. */
+	if (filed.fd_cdir) {
+		entry = filestat_new_entry(filed.fd_cdir, PS_FST_TYPE_VNODE, -1,
+		    PS_FST_FFLAG_READ, PS_FST_UFLAG_CDIR, 0, 0, NULL);
+		if (entry != NULL)
+			STAILQ_INSERT_TAIL(head, entry, next);
+	}
+	/* jail root, if any. */
+	if (filed.fd_jdir) {
+		entry = filestat_new_entry(filed.fd_jdir, PS_FST_TYPE_VNODE, -1,
+		    PS_FST_FFLAG_READ, PS_FST_UFLAG_JAIL, 0, 0, NULL);
+		if (entry != NULL)
+			STAILQ_INSERT_TAIL(head, entry, next);
+	}
+	/* ktrace vnode, if one */
+	if (kp->ki_tracep) {
+		entry = filestat_new_entry(kp->ki_tracep, PS_FST_TYPE_VNODE, -1,
+		    PS_FST_FFLAG_READ | PS_FST_FFLAG_WRITE,
+		    PS_FST_UFLAG_TRACE, 0, 0, NULL);
+		if (entry != NULL)
+			STAILQ_INSERT_TAIL(head, entry, next);
+	}
+	/* text vnode, if one */
+	if (kp->ki_textvp) {
+		entry = filestat_new_entry(kp->ki_textvp, PS_FST_TYPE_VNODE, -1,
+		    PS_FST_FFLAG_READ, PS_FST_UFLAG_TEXT, 0, 0, NULL);
+		if (entry != NULL)
+			STAILQ_INSERT_TAIL(head, entry, next);
+	}
+	/* Controlling terminal. */
+	if ((vp = getctty(kd, kp)) != NULL) {
+		entry = filestat_new_entry(vp, PS_FST_TYPE_VNODE, -1,
+		    PS_FST_FFLAG_READ | PS_FST_FFLAG_WRITE,
+		    PS_FST_UFLAG_CTTY, 0, 0, NULL);
+		if (entry != NULL)
+			STAILQ_INSERT_TAIL(head, entry, next);
+	}
+
+	nfiles = filed.fd_lastfile + 1;
+	ofiles = malloc(nfiles * sizeof(struct file *));
+	if (ofiles == NULL) {
+		warn("malloc(%zd)", nfiles * sizeof(struct file *));
+		goto do_mmapped;
+	}
+	if (!kvm_read_all(kd, (unsigned long)filed.fd_ofiles, ofiles,
+	    nfiles * sizeof(struct file *))) {
+		warnx("cannot read file structures at %p",
+		    (void *)filed.fd_ofiles);
+		free(ofiles);
+		goto do_mmapped;
+	}
+	for (i = 0; i <= filed.fd_lastfile; i++) {
+		if (ofiles[i] == NULL)
+			continue;
+		if (!kvm_read_all(kd, (unsigned long)ofiles[i], &file,
+		    sizeof(struct file))) {
+			warnx("can't read file %d at %p", i,
+			    (void *)ofiles[i]);
+			continue;
+		}
+		switch (file.f_type) {
+		case DTYPE_VNODE:
+			type = PS_FST_TYPE_VNODE;
+			data = file.f_vnode;
+			break;
+		case DTYPE_SOCKET:
+			type = PS_FST_TYPE_SOCKET;
+			data = file.f_data;
+			break;
+		case DTYPE_PIPE:
+			type = PS_FST_TYPE_PIPE;
+			data = file.f_data;
+			break;
+		case DTYPE_FIFO:
+			type = PS_FST_TYPE_FIFO;
+			data = file.f_vnode;
+			break;
+#ifdef DTYPE_PTS
+		case DTYPE_PTS:
+			type = PS_FST_TYPE_PTS;
+			data = file.f_data;
+			break;
+#endif
+		default:
+			continue;
+		}
+		entry = filestat_new_entry(data, type, i,
+		    to_filestat_flags(file.f_flag), 0, 0, 0, NULL);
+		if (entry != NULL)
+			STAILQ_INSERT_TAIL(head, entry, next);
+	}
+	free(ofiles);
+
+do_mmapped:
+
+	/*
+	 * Process mmapped files if requested.
+	 */
+	if (mmapped) {
+		if (!kvm_read_all(kd, (unsigned long)kp->ki_vmspace, &vmspace,
+		    sizeof(vmspace))) {
+			warnx("can't read vmspace at %p",
+			    (void *)kp->ki_vmspace);
+			goto exit;
+		}
+		map = &vmspace.vm_map;
+
+		for (entryp = map->header.next;
+		    entryp != &kp->ki_vmspace->vm_map.header;
+		    entryp = vmentry.next) {
+			if (!kvm_read_all(kd, (unsigned long)entryp, &vmentry,
+			    sizeof(vmentry))) {
+				warnx("can't read vm_map_entry at %p",
+				    (void *)entryp);
+				continue;
+			}
+			if (vmentry.eflags & MAP_ENTRY_IS_SUB_MAP)
+				continue;
+			if ((objp = vmentry.object.vm_object) == NULL)
+				continue;
+			for (; objp; objp = object.backing_object) {
+				if (!kvm_read_all(kd, (unsigned long)objp,
+				    &object, sizeof(object))) {
+					warnx("can't read vm_object at %p",
+					    (void *)objp);
+					break;
+				}
+			}
+
+			/* We want only vnode objects. */
+			if (object.type != OBJT_VNODE)
+				continue;
+
+			prot = vmentry.protection;
+			fflags = 0;
+			if (prot & VM_PROT_READ)
+				fflags = PS_FST_FFLAG_READ;
+			if (prot & VM_PROT_WRITE)
+				fflags |= PS_FST_FFLAG_WRITE;
+
+			/*
+			 * Create filestat entry.
+			 */
+			entry = filestat_new_entry(object.handle,
+			    PS_FST_TYPE_VNODE, -1, fflags,
+			    PS_FST_UFLAG_MMAP, 0, 0, NULL);
+			if (entry != NULL)
+				STAILQ_INSERT_TAIL(head, entry, next);
+		}
+	}
+exit:
+	return (head);
+}
+
+/*
+ * kinfo types to filestat translation.
+ */
+static int
+kinfo_type2fst(int kftype)
+{
+	static struct {
+		int	kf_type;
+		int	fst_type;
+	} kftypes2fst[] = {
+		{ KF_TYPE_CRYPTO, PS_FST_TYPE_CRYPTO },
+		{ KF_TYPE_FIFO, PS_FST_TYPE_FIFO },
+		{ KF_TYPE_KQUEUE, PS_FST_TYPE_KQUEUE },
+		{ KF_TYPE_MQUEUE, PS_FST_TYPE_MQUEUE },
+		{ KF_TYPE_NONE, PS_FST_TYPE_NONE },
+		{ KF_TYPE_PIPE, PS_FST_TYPE_PIPE },
+		{ KF_TYPE_PTS, PS_FST_TYPE_PTS },
+		{ KF_TYPE_SEM, PS_FST_TYPE_SEM },
+		{ KF_TYPE_SHM, PS_FST_TYPE_SHM },
+		{ KF_TYPE_SOCKET, PS_FST_TYPE_SOCKET },
+		{ KF_TYPE_VNODE, PS_FST_TYPE_VNODE },
+		{ KF_TYPE_UNKNOWN, PS_FST_TYPE_UNKNOWN }
+	};
+#define NKFTYPES	(sizeof(kftypes2fst) / sizeof(*kftypes2fst))
+	unsigned int i;
+
+	for (i = 0; i < NKFTYPES; i++)
+		if (kftypes2fst[i].kf_type == kftype)
+			break;
+	if (i == NKFTYPES)
+		return (PS_FST_TYPE_UNKNOWN);
+	return (kftypes2fst[i].fst_type);
+}
+
+/*
+ * kinfo flags to filestat translation.
+ */
+static int
+kinfo_fflags2fst(int kfflags)
+{
+	static struct {
+		int	kf_flag;
+		int	fst_flag;
+	} kfflags2fst[] = {
+		{ KF_FLAG_APPEND, PS_FST_FFLAG_APPEND },
+		{ KF_FLAG_ASYNC, PS_FST_FFLAG_ASYNC },
+		{ KF_FLAG_CREAT, PS_FST_FFLAG_CREAT },
+		{ KF_FLAG_DIRECT, PS_FST_FFLAG_DIRECT },
+		{ KF_FLAG_EXCL, PS_FST_FFLAG_EXCL },

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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