[comp.sys.sequent] new ofiles for vnode file systems: 4.3/nfs, sun, sequent

rsk@j.cc.purdue.edu.UUCP (05/14/87)

I have taken ofiles and rewritten it to work correctly for remote filesystems,
character special devices, and block special devices (as devices).  I have
tested it on Sun 3.2 and 4.3+NFS (Wisconsin), but I'm sure I haven't got it
quite right for the sequent (we don't have one).  I certainly don't have the
#includes right.  Perhaps someone with access to a sequent could get it working
and mail me their working copy, so I can include it in the further enhancements
I am making.  I used getopt() - if you don't have a copy you can get it from
any mod.sources archive or send me mail and I'll send you a copy.

This posting doesn't include a man page - the only added flag is "-f" which
prevents looking up arguments in /etc/mtab - useful if you want to see if
anyone has your filesystems' block special devices open as devices.

Because of the the way /dev/tty is implemented, you can't say "ofiles /dev/tty"
to find processes with their controlling tty open.  But "ofiles /dev/ttya" or
"ofiles /dev/null" works fine.

@alex
---
arpanet: dupuy@columbia.edu
uucp:	...!seismo!columbia!dupuy

: This is a shar archive.  Extract with sh, not csh.
: The rest of this file will extract: 
:
:	ofiles.c
:
echo x - ofiles.c
sed 's/^X//' > ofiles.c << '//go.sysin dd *'
X#include <sys/param.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#ifdef sequent
X# define KERNEL
X# include <sys/file.h>
X# include <sys/vnode.h>
X# include <sys/vfs.h>
X# include <sys/inode.h>
X# include <rpc/types.h>
X# include <nfs/nfs.h>
X# include <netinet/in.h>
X# include <nfs/nfs_clnt.h>
X# include <nfs/rnode.h>
X# undef KERNEL
X#else
X# define KERNEL
X# include <sys/file.h>
X# undef KERNEL
X# include <sys/vnode.h>
X# include <sys/vfs.h>
X# include <ufs/inode.h>
X# include <rpc/types.h>
X# include <nfs/nfs.h>
X# include <netinet/in.h>
X# include <nfs/nfs_clnt.h>
X# include <nfs/rnode.h>
X#endif
X#include <machine/pte.h>
X#include <sys/vmmac.h>
X#include <sys/proc.h>
X#include <nlist.h>
X#include <sys/stat.h>
X#include <strings.h>
X#include <stdio.h>
X#include <mntent.h>
X#include <pwd.h>
X
Xunion fileid
X{
X    struct
X    {
X	long            fsid;
X	long            nodeid;
X    }               file;
X    struct
X    {
X	dev_t           dev;
X	enum vtype      type;
X    }               special;
X};
X
X#define CDIR	01
X#define OFILE	02
X#define RDIR	04
X
Xlong            eseek ();
Xint             eread ();
Xchar           *vtype ();
Xchar           *itype ();
Xchar           *rtype ();
Xstruct mntent  *getmntname ();
Xstruct mntent  *getmntfile ();
Xunion fileid   *getfileid ();
Xstruct user    *getuser ();
X
X#define error(s)	(perror ((s) ? (s) : ""), exit (1))
X
X#define istype(mode,type)	(((mode) & S_IFMT) == (type))
X#define ispecial(mode)	(istype ((mode), S_IFBLK) || istype ((mode), S_IFCHR))
X
X#define vspecial(vtype)	((vtype) == VCHR || (vtype) == VBLK)
X
X#define nfsdev(rdev)	(major (rdev) == 0xff)
X
X/*
X * File descriptors. 
X */
X
Xint             mem;			/* fd for /dev/mem */
Xint             kmem;			/* fd for /dev/kmem */
Xint             swap;			/* fd for /dev/drum */
X
XFILE           *mtab;			/* stdio file for /etc/mtab */
X
X/*
X * Kernel tables. 
X */
X
X#ifdef sequent
X# define UNIX "/dynix"
X#else
X# define UNIX "/vmunix"
X#endif
Xstruct nlist    nl[] =
X{
X#define X_PROC		0
X {"_proc"},
X#define X_NPROC 	1
X {"_nproc"},
X#define X_USRPTMA	2
X {"_Usrptmap"},
X#define X_USRPT		3
X {"_usrpt"},
X {0}
X};
Xlong            procbase;		/* address of process table */
Xint             nproc;			/* number of entries in proc table */
Xstruct pte     *Usrptma,		/* user page table map */
X               *usrpt;			/* user page table  */
X
Xint             inodes[NOFILE];		/* inodes of open files */
X
X/*
X * Program options. 
X */
X
Xchar           *progname;
Xint             terse = 0;		/* if non-zero, only output pids */
Xint             debug = 10;		/* turn on debugging output */
Xint             filesonly = 0;		/* match args as files, not mntents */
Xint             inode = 0;		/* if non-zero, inode to match */
X
Xmain (argc, argv)
Xint             argc;
Xchar           *argv[];
X{
X
X    char            filename[MNTMAXSTR],
X                    fsname[MNTMAXSTR];
X    struct stat     stats;
X    struct mntent  *mnt;
X
X    extern char    *optarg;
X    extern int      optind;
X    int             option;
X    int             usage = 0;
X
X    progname = argv[0];
X
X    while ((option = getopt (argc, argv, "pfd:")) != EOF)
X    {
X	switch (option)
X	{
X	  case 'p':
X	    terse = 1;
X	    break;
X
X	  case 'f':
X	    filesonly = 1;
X	    break;
X
X	  case 'd':
X	    debug = atoi (optarg);
X	    break;
X
X	  case '?':
X	    usage = 1;
X	}
X    }
X    if (!(argc -= optind) || usage)
X    {
X	fprintf (stderr, "usage: %s [-p] files\n", progname);
X	exit (1);
X    }
X
X    if ((mem = open ("/dev/mem", 0)) < 0)
X	error ("can't open /dev/mem. ");
X    if ((kmem = open ("/dev/kmem", 0)) < 0)
X	error ("can't open /dev/kmem. ");
X    if ((swap = open ("/dev/drum", 0)) < 0)
X	error ("can't open /dev/drum. ");
X
X    if ((mtab = setmntent (MOUNTED, "r")) == 0)
X	error ("can't open /etc/mtab. ");
X
X    getsyms ();
X
X    for (argv += optind; argc--; argv++)
X    {
X	/*
X	 * Check for filesystem/mountpoint vs. file. 
X	 */
X
X	if (!filesonly && (mnt = getmntname (*argv)) != NULL)
X	{
X	    inode = 0;			/* match any inode on this fs */
X	    strcpy (filename, mnt->mnt_dir);
X	    strcpy (fsname, mnt->mnt_fsname);
X	}
X	else
X	{
X	    inode = 1;			/* match only one inode */
X	    strcpy (filename, *argv);
X	}
X
X	if (stat (filename, &stats))	/* find out what it is we want */
X	{
X	    perror (filename);
X	    continue;
X	}
X
X	if (inode)			/* looking for file */
X	{
X	    inode = stats.st_ino;
X	    if ((mnt = getmntfile (&stats)) != NULL)
X		strcpy (fsname, mnt->mnt_fsname);
X	    else
X		strcpy (fsname, "unknown");
X	}
X
X	if (!terse)
X	{
X	    printf ("%s:\t%s\n", fsname, filename);
X
X	    if (debug > 5)
X		if (ispecial (stats.st_mode))
X		    printf (" %s device %d/%d\n", itype (stats.st_mode),
X			    major (stats.st_rdev), minor (stats.st_rdev));
X		else if (nfsdev (stats.st_dev))
X		    printf (" inode %d on remote fs %d (%s)\n",
X			    stats.st_ino, minor (stats.st_dev),
X			    itype (stats.st_mode));
X		else
X		    printf (" inode %d on block device %x (%s)\n",
X			    stats.st_ino, stats.st_dev, itype (stats.st_mode));
X
X	    printf ("user\t process command\ttype\tinode(s)\n");
X	}
X
X	lookfor (&stats);
X    }
X}
X
Xstruct mntent  *
Xgetmntname (name)
Xchar           *name;
X{
X    register struct mntent *mnt;
X
X    rewind (mtab);
X    while ((mnt = getmntent (mtab)) != 0)
X    {
X	if (strcmp (name, mnt->mnt_fsname) == 0)
X	    return (mnt);
X	if (strcmp (name, mnt->mnt_dir) == 0)
X	    return (mnt);
X    }
X    return (NULL);
X}
X
Xstruct mntent  *
Xgetmntfile (filestats)
Xstruct stat    *filestats;
X{
X    register struct mntent *mnt;
X    struct stat     dirstats;
X
X    rewind (mtab);
X    while ((mnt = getmntent (mtab)) != 0)
X	if ((stat (mnt->mnt_dir, &dirstats) >= 0) &&
X	    (filestats->st_dev == dirstats.st_dev))
X	    return (mnt);
X
X    return (NULL);
X}
X
Xlookfor (filestats)
Xstruct stat    *filestats;
X{
X    struct proc     p;
X    struct user    *u;
X    register int    procn,
X                    flags,
X                    filen;
X
X    for (procn = 0; procn < nproc; procn++)
X    {
X	procslot (procn, &p);
X	flags = 0;
X	if (p.p_stat == 0 || p.p_stat == SZOMB)
X	    continue;
X	u = getuser (&p);
X	if (u == (struct user *) NULL)
X	    continue;
X
X	if (debug > 10)
X	    printf ("uid %d pid %d command %s\n", p.p_uid, p.p_pid, u->u_comm);
X
X	if (u->u_rdir != NULL)
X	    if (check (filestats, getfileid (u->u_rdir, "rdir")))
X		flags |= RDIR;
X
X	if (u->u_cdir != NULL)
X	    if (check (filestats, getfileid (u->u_cdir, "cdir")))
X		flags |= CDIR;
X
X	for (filen = 0; filen < NOFILE; filen++)
X	{
X	    struct file     f;
X	    union fileid   *fid;
X
X	    inodes[filen] = 0;
X
X	    if (u->u_ofile[filen] == NULL)
X		continue;
X
X	    eseek (kmem, (long) u->u_ofile[filen], 0, "file");
X	    eread (kmem, (char *) &f, sizeof (f), "file");
X
X	    if (f.f_count > 0)
X	    {
X		if (f.f_type != DTYPE_VNODE)
X		    continue;
X
X		fid = getfileid ((struct vnode *) f.f_data, "ofile");
X		if (check (filestats, fid))
X		{
X		    flags |= OFILE;
X		    if (!ispecial (filestats->st_mode))
X			inodes[filen] = fid->file.nodeid;
X		}
X	    }
X	}
X	if (flags)
X	    gotone (u, &p, flags);
X    }
X}
X
X/*
X * Print the name of the user owning process "p" and the pid of that process 
X */
X
Xgotone (u, p, f)
Xstruct user    *u;
Xstruct proc    *p;
Xint             f;
X{
X    struct passwd  *getpwuid ();
X    register struct passwd *pw;
X    register int    filen;
X
X    if (terse)				/* only print pids and return */
X    {
X	printf ("%d ", p->p_pid);
X	fflush (stdout);
X	return;
X    }
X
X    pw = getpwuid (p->p_uid);
X    if (pw)
X	printf ("%-8.8s", pw->pw_name);
X    else
X	printf ("(%d)\t", p->p_uid);
X
X    printf (" %d\t", p->p_pid);
X
X    printf (" %-14.14s\t", u->u_comm);
X
X    if (f & CDIR)
X	putchar ('c');			/* proc's current dir is on device */
X    if (f & RDIR)
X	putchar ('r');			/* proc's root dir is on device */
X    if (f & OFILE)
X	putchar ('f');			/* proc has file(s) open on device */
X    printf ("\t");
X
X    if (f & OFILE)			/* list inodes of open file(s) */
X	for (filen = 0; filen < NOFILE; filen++)
X	    if (inodes[filen] > 0)
X		printf ("%d ", inodes[filen]);
X
X    puts ("");				/* silly way to print a newline */
X}
X
X
X/*
X * Check if file id matches stats we are searching for. 
X */
X
Xcheck (filestats, fid)
Xstruct stat    *filestats;
Xunion fileid   *fid;
X{
X    if (fid == NULL)
X	return (0);
X
X    if (ispecial (filestats->st_mode))
X    {
X	if (filestats->st_rdev == fid->special.dev
X	    && !strcmp (itype (filestats->st_mode), vtype (fid->special.type)))
X	    return (1);			/* for special devices */
X	else
X	    return (0);
X    }
X
X    if (filestats->st_dev == fid->file.fsid)
X    {
X	if (!inode)			/* if we'll match any file */
X	    return (1);
X
X	if (inode == fid->file.nodeid)
X	    return (1);
X#ifdef sun				/* not sure I understand this */
X	else if (fid->file.nodeid == 0)
X	    return (1);
X#endif
X    }
X
X    return (0);
X}
X
X/*
X * Read a vnode from /dev/kmem, return fileid (dev,ino). 
X */
X
Xunion fileid   *
Xgetfileid (node, s)
Xstruct vnode   *node;
Xchar           *s;
X{
X    struct vnode v;
X    static union xnode
X    {
X	struct inode    i;
X	struct rnode    r;
X    }               x;
X
X    union fileid    fid;
X
X    if (node == NULL)
X	return (NULL);
X
X    eseek (kmem, (long) node, 0, "vnode");
X    eread (kmem, (char *) &v, sizeof (v), "vnode");
X
X    if (vspecial (v.v_type))
X    {
X	fid.special.dev = v.v_rdev;
X	fid.special.type = v.v_type;
X
X	if (debug > 20)
X	    printf (" %s %s device %d/%d\n",
X		    s, vtype (v.v_type), major (v.v_rdev), minor (v.v_rdev));
X	return (&fid);
X    }
X
X    if (debug > 20)
X	printf (" %s %s", s, vtype (v.v_type));
X
X    eseek (kmem, (long) v.v_data, 0, "inode/rnode");
X    eread (kmem, (char *) &x, sizeof (x), "inode/rnode");
X
X    if (x.r.r_nfsattr.na_type == v.v_type)
X    {					/* best test I could find for nfs */
X	struct vfs vf;
X	struct mntinfo mi;
X
X	eseek (kmem, (long) v.v_vfsp, 0, "vfs");
X	eread (kmem, (char *) &vf, sizeof (vf), "vfs");
X
X	eseek (kmem, (long) vf.vfs_data, 0, "mntinfo");
X	eread (kmem, (char *) &mi, sizeof (mi), "mntinfo");
X
X	fid.file.fsid = makedev (0xff, mi.mi_mntno);
X	fid.file.nodeid = x.r.r_nfsattr.na_nodeid;
X
X	if (debug > 20)
X	    printf (" inode %d on nfs fs %x (%s)\n",
X		    x.r.r_nfsattr.na_nodeid, x.r.r_nfsattr.na_fsid,
X		    rtype (x.r.r_nfsattr.na_type));
X    }
X    else
X    {
X	fid.file.fsid = x.i.i_dev;
X	fid.file.nodeid = x.i.i_number;
X
X	if (debug > 20)
X	    printf (" inode %d on device %x (%s)\n",
X		    x.i.i_number, x.i.i_dev, itype (x.i.i_mode));
X    }
X
X    return (&fid);
X}
X
X
Xchar           *
Xvtype (type)
Xenum vtype      type;
X{
X    switch (type)
X    {
X      case VNON:
X	return ("none");
X      case VREG:
X	return ("file");
X      case VDIR:
X	return ("directory");
X      case VBLK:
X	return ("block special");
X      case VCHR:
X	return ("character special");
X      case VLNK:
X	return ("symbolic link");
X      case VSOCK:
X	return ("socket");
X      case VBAD:
X	return ("bad type");
X#ifdef S_IFIFO
X      case VFIFO:
X	return ("fifo");
X#endif
X      default:
X	return ("unknown vnode type");
X    }
X}
X
Xchar           *
Xrtype (type)
Xenum nfsftype   type;
X{
X    switch (type)
X    {
X      case NFNON:
X	return ("none");
X      case NFREG:
X	return ("file");
X      case NFDIR:
X	return ("directory");
X      case NFBLK:
X	return ("block special");
X      case NFCHR:
X	return ("character special");
X      case NFLNK:
X	return ("symbolic link");
X      default:
X	return ("unknown");
X    }
X}
X
Xchar           *
Xitype (mode)
Xunsigned        mode;
X{
X    switch (mode & S_IFMT)
X    {
X      case S_IFREG:
X	return ("file");
X      case S_IFDIR:
X	return ("directory");
X      case S_IFBLK:
X	return ("block special");
X      case S_IFCHR:
X	return ("character special");
X      case S_IFLNK:
X	return ("symbolic link");
X      case S_IFSOCK:
X	return ("socket");
X#ifdef S_IFIFO
X      case S_IFIFO:
X	return ("fifo");
X#endif
X      default:
X	return ("unknown inode type");
X    }
X}
X
X/*
X * get user page for proc "p" from core or swap return pointer to user struct 
X */
Xstruct user    *
Xgetuser (p)
Xstruct proc    *p;
X{
X    struct pte     *ptep;
X    struct pte      mypgtbl[UPAGES];
X    int             n,
X                    nbytes;
X    char           *up;
X    static struct user user;
X
X    /* easy way */
X    if ((p->p_flag & SLOAD) == 0)
X    {
X	eseek (swap, (long) dtob (p->p_swaddr), 0, "user");
X	if (read (swap, (char *) &user, sizeof (struct user)) <= 0)
X	{
X	    if (debug)
X		fprintf (stderr, "error: can't get swapped user page pid %d\n",
X			 p->p_pid);
X	    return (struct user *) NULL;
X	}
X	if (debug > 50)
X	    printf ("read swap\n");
X    }
X    else
X    {					/* boo */
X	/* now get user page tbl */
X#ifdef sequent
X	eseek (mem, p->p_upte, 0L, "user page table");
X	if (read (mem, mypgtbl, sizeof (mypgtbl)) != sizeof (mypgtbl))
X	{
X#else
X	eseek (kmem, p->p_addr, 0L, "user page table");
X	if (read (kmem, mypgtbl, sizeof (mypgtbl)) != sizeof (mypgtbl))
X	{
X#endif
X	    if (debug)
X		fprintf (stderr, "error: can't get mypgtbl pid %d\n",
X			 p->p_pid);
X	    return (struct user *) NULL;
X	}
X	/* now collect pages of u area */
X	up = (char *) &user;
X	ptep = mypgtbl;
X	for (nbytes = sizeof (struct user); nbytes > 0; nbytes -= NBPG)
X	{
X	    eseek (mem, ptep++->pg_pfnum * NBPG, 0L, "page table entry");
X	    n = MIN (nbytes, NBPG);
X	    if (read (mem, up, n) != n)
X	    {
X		if (debug)
X		    fprintf (stderr, "cant get user tbl pid %d\n", p->p_pid);
X		return (struct user *) NULL;
X	    }
X	    up += n;
X	}
X    }
X    return &user;
X}
X
X
X/*
X * Get symbols from the kernel. 
X */
X
Xgetsyms ()
X{
X    register        i;
X
X    if (nlist (UNIX, nl))
X	perror (UNIX), exit (1);
X
X    for (i = 0; i < (sizeof (nl) / sizeof (nl[0])) - 1; i++)
X	if (nl[i].n_value == 0)
X	{
X	    fprintf (stderr, "%s: can't nlist for %s.\n",
X		     progname, nl[i].n_name);
X	    exit (1);
X	}
X
X    eseek (kmem, (long) nl[X_PROC].n_value, 0, "procbase");
X    eread (kmem, &procbase, (unsigned) sizeof (procbase), "procbase");
X
X    eseek (kmem, (long) nl[X_NPROC].n_value, 0, "nproc");
X    eread (kmem, &nproc, sizeof (nproc), "nproc");
X
X    /* variables used by <vmmac.h> */
X
X    Usrptma = (struct pte *) nl[X_USRPTMA].n_value;
X    usrpt = (struct pte *) nl[X_USRPT].n_value;
X}
X
X
X/*
X * Read proc table entry "n" into buffer "p" . 
X */
Xprocslot (n, p)
Xint             n;
Xstruct proc    *p;
X{
X    eseek (kmem, procbase + (long) (n * sizeof (struct proc)), 0, "proc");
X    eread (kmem, (char *) p, sizeof (struct proc), "proc");
X}
X
X/*
X * Read with error checking 
X */
Xeread (fd, p, size, s)
Xint             fd;
Xchar           *p;
Xint             size;
Xchar           *s;
X{
X    int             n;
X    char            buf[100];
X    if ((n = read (fd, p, size)) != size)
X    {
X	sprintf (buf, "read error for %s. ", s);
X	error (buf);
X    }
X    return n;
X}
X
X/*
X * Seek with error checking 
X */
X
Xlong
Xeseek (fd, off, whence, s)
Xint             fd;
Xlong            off;
Xint             whence;
Xchar           *s;
X{
X    long            lseek ();
X    long            ret;
X    char            buf[100];
X
X    if ((ret = lseek (fd, off, whence)) != off)
X    {
X	sprintf (buf, "seek for %s failed, wanted %o, got %o. ",
X		 s, off, ret);
X	error (buf);
X    }
X    return ret;
X}
//go.sysin dd *
exit