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